This Rmarkdown file assesses the output of CheckV, DeepVirFinder, Kaiju, VIBRANT, VirSorter, and VirSorter2 on multiple training sets of microbial DNA, primarily from NCBI. Created from fungal, viral, bacterial, archeael, protist, and plasmid DNA sequences

Please reach out to James Riddell () or Bridget Hegarty () regarding any issues, or open an issue on github.

library(ggplot2)
There were 43 warnings (use warnings() to see them)
library(plyr)
library(reshape2)
library(viridis)
library(tidyr)
library(dplyr)
library(readr)
library(data.table)
library(pROC)

Import the file that combines the results from each of the tools from running “combining_tool_output.Rmd”:

viruses <- read_tsv("../IntermediaryFiles/viral_tools_combined.tsv")

── Column specification ──────────────────────────────────────────────────────────────────────────
cols(
  .default = col_double(),
  seqtype = col_character(),
  contig = col_character(),
  checkv_provirus = col_character(),
  checkv_quality = col_character(),
  method.x = col_character(),
  Classified = col_character(),
  IDs_all = col_character(),
  Seq = col_character(),
  Kaiju_Viral = col_character(),
  Kingdom = col_character(),
  type = col_character(),
  vibrant_quality = col_character(),
  method.y = col_character(),
  vibrant_prophage = col_character(),
  vs2type = col_character(),
  max_score_group = col_character(),
  provirus = col_logical()
)
ℹ Use `spec()` for the full column specifications.

This section defines a viralness score “keep_score” based on the tool classifications. A final keep_score above 1 indicates we will keep that sequence and call it viral.

VIBRANT Quality == “Complete Circular”: +1 Quality == “High Quality Draft”: +1 Quality == “Medium Quality Draft”: +1 Quality == “Low Quality Draft” & provirus: +0.5

Virsorter2 Viral >= 50: +0.5 Viral >= 0.95: +0.5 RNA >= 0.9: +1 lavidaviridae >= 0.9: +1 NCLDV >= 0.9: +1

Virsorter category == 1,4: +1 category == 2,5: +0.5

DeepVirFinder: Score >= 0.7: +0.5

Tuning - No Viral Signature: Kaiju_viral = “cellular organisms”: -0.5 If host_genes >50 and NOT provirus: -1 If viral_genes == 0 and host_genes >= 1: -1 If 3*viral_genes <= host_genes and NOT provirus: -1 If length > 50,000 and hallmark <=1: -1 If length < 5000 and checkv completeness <= 75: -0.5

Tuning - Viral Signature: Kaiju_viral = “Viruses”: +1 If %unknown >= 75 and length < 50000: +0.5 If %viral >= 50: +0.5 Hallmark > 2: +1

This script produces visualizations of these combined viral scorings and includes ecological metrics like alpha diversity.

You can decide which combination is appropriate for them and only need use the tools appropriate for your data.

getting_viral_set_1 <- function(input_seqs,
                                include_vibrant=FALSE, 
                                include_virsorter2=FALSE,
                                include_deepvirfinder=FALSE,
                                include_tuning_viral=FALSE,
                                include_tuning_not_viral=FALSE,
                                include_virsorter=FALSE) {
  
  keep_score <- rep(0, nrow(input_seqs))
  
  if (include_vibrant) {
    keep_score[input_seqs$vibrant_quality=="complete circular"] <- keep_score[input_seqs$vibrant_quality=="complete circular"] + 1
    keep_score[input_seqs$vibrant_quality=="high quality draft"] <- keep_score[input_seqs$vibrant_quality=="high quality draft"] + 1
    keep_score[input_seqs$vibrant_quality=="medium quality draft"] <- keep_score[input_seqs$vibrant_quality=="medium quality draft"] + 1
    keep_score[input_seqs$vibrant_quality=="low quality draft" & input_seqs$provirus] <- keep_score[input_seqs$vibrant_quality=="low quality draft" & input_seqs$provirus] + 0.5
  }
  
  if (include_virsorter2) {
    keep_score[input_seqs$viral>=50] <- keep_score[input_seqs$viral>=50] + 0.5
    keep_score[input_seqs$viral>=95] <- keep_score[input_seqs$viral>=95] + 0.5
    keep_score[input_seqs$RNA>=0.9] <- keep_score[input_seqs$RNA>=0.9] + 1
    keep_score[input_seqs$lavidaviridae>=0.9] <- keep_score[input_seqs$lavidaviridae>=0.9] + 1
    keep_score[input_seqs$NCLDV>=0.9] <- keep_score[input_seqs$NCLDV>=0.9] + 1
  }
  
  if (include_virsorter) {
    keep_score[input_seqs$category==1] <- keep_score[input_seqs$category==1] + 1
    keep_score[input_seqs$category==2] <- keep_score[input_seqs$category==2] + 0.5
    keep_score[input_seqs$category==4] <- keep_score[input_seqs$category==4] + 1
    keep_score[input_seqs$category==5] <- keep_score[input_seqs$category==5] + 0.5
  }
  
  if (include_deepvirfinder) {
    keep_score[input_seqs$score>=0.7 & input_seqs$checkv_length<20000] <- keep_score[input_seqs$score>=0.7 & input_seqs$checkv_length<20000] + 0.5
    keep_score[input_seqs$score>=0.9 & input_seqs$checkv_length<20000] <- keep_score[input_seqs$score>=0.9 & input_seqs$checkv_length<20000] + 0.5
  }
  
  if (include_tuning_viral) {
    keep_score[input_seqs$Kaiju_Viral=="Viruses"] <- keep_score[input_seqs$Kaiju_Viral=="Viruses"] + 0.5
    keep_score[input_seqs$hallmark>2] <- keep_score[input_seqs$hallmark>2] + 1
    keep_score[input_seqs$percent_unknown>=75 & input_seqs$checkv_length<50000] <- keep_score[input_seqs$percent_unknown>=75 & input_seqs$checkv_length<50000] + 0.5
    keep_score[input_seqs$percent_viral>=50] <- keep_score[input_seqs$percent_viral>=50] + 0.5
  }
  
  if (include_tuning_not_viral) {
    keep_score[input_seqs$Kaiju_Viral=="cellular organisms"] <- keep_score[input_seqs$Kaiju_Viral=="cellular organisms"] - 0.5
    keep_score[input_seqs$checkv_host_genes>50 & !input_seqs$provirus] <- keep_score[input_seqs$checkv_host_genes>50 & !input_seqs$provirus] - 1
    keep_score[input_seqs$checkv_viral_genes==0 & input_seqs$checkv_host_genes>=1] <- keep_score[input_seqs$checkv_viral_genes==0 & input_seqs$checkv_host_genes>=1] - 1
    keep_score[((input_seqs$checkv_viral_genes*3) <= input_seqs$checkv_host_genes) & !input_seqs$provirus] <- keep_score[((input_seqs$checkv_viral_genes*3) <= input_seqs$checkv_host_genes) & !input_seqs$provirus] - 1 # consider accounting for RNA viruses
    keep_score[input_seqs$checkv_length>500000 & input_seqs$hallmark<=1] <- keep_score[input_seqs$checkv_length>500000 & input_seqs$hallmark<=1] - 1
    keep_score[input_seqs$checkv_completeness<=75 & input_seqs$checkv_length<=5000] <- keep_score[input_seqs$checkv_completeness<=75 & input_seqs$checkv_length<=5000] - 0.5 # helped with protist contamination
  }
  
  return(keep_score)
  
}

Assessing performance against the “truth”

note that this is only as accurate as the annotations of the input sequences

this function calculates the precision, recall, and F1 score for each pipeline

assess_performance <- function(seqtype, keep_score) {
  
  truepositive <- rep("not viral", length(seqtype))
  truepositive[seqtype=="virus"] <- "viral"
  
  #make confusion matrix
  confusion_matrix <- rep("true negative", length(keep_score))
  confusion_matrix[truepositive=="viral" & keep_score<=1] <- "false negative"
  confusion_matrix[truepositive=="viral" & keep_score>=1] <- "true positive"
  confusion_matrix[truepositive=="not viral" & keep_score>=1] <- "false positive"
  
  TP <- table(confusion_matrix)[4]
  FP <- table(confusion_matrix)[2]
  TN <- table(confusion_matrix)[3]
  FN <- table(confusion_matrix)[1]
  
  precision <- TP/(TP+FP)
  recall <- TP/(TP+FN)
  F1 <- 2*precision*recall/(precision+recall)
  
  MCC <- (TP*TN-FP*FN)/sqrt(as.numeric(TP+FP)*as.numeric(TP+FN)*as.numeric(TN+FP)*as.numeric(TN+FN))
  
  auc <- round(auc(truepositive, keep_score),4)
  
  #by type metrics
  fungal_FP <- table(confusion_matrix[seqtype=="fungi"])[2]
  protist_FP <- table(confusion_matrix[seqtype=="protist"])[2]
  bacterial_FP <- table(confusion_matrix[seqtype=="bacteria"])[2]
  viral_FN <- table(confusion_matrix[seqtype=="virus"])[1]
  
  performance <- c(precision, recall, F1, MCC, auc, fungal_FP, 
                   protist_FP, bacterial_FP, viral_FN)
  names(performance) <- c("precision", "recall", "F1", "MCC", "AUC", "fungal_FP",
                          "protist_FP", "bacterial_FP", "viral_FN")
  
  return(performance)
}

combination of tools list

combos_list <- data.frame(toolcombo=rep(0, 64),
                          tune_not_viral=rep(0, 64),
                          DVF=rep(0, 64),
                          tune_viral=rep(0, 64),
                          VIBRANT=rep(0, 64),
                          VS=rep(0, 64),
                          VS2=rep(0, 64))
p <- 1

for (i in c(0,1)){
  for (j in c(0,1)){
    for (k in c(0,1)){
      for (l in c(0,1)){
        for (m in c(0,1)){
          for (n in c(0,1)){
            combos_list$toolcombo[p] <- paste(i,j,k,l,m,n)
            combos_list$toolcombo2[p] <- paste(if(i){"tv"}else{"0"},if(j){"DVF"}else{"0"},
                                               if(k){"tnv"}else{"0"},if(l){"VB"}else{"0"},
                                               if(m){"VS"}else{"0"},if(n){"VS2"}else{"0"})
            combos_list$tune_not_viral[p] <- i
            combos_list$DVF[p] <- j
            combos_list$tune_viral[p] <- k
            combos_list$VIBRANT[p] <- l
            combos_list$VS[p] <- m
            combos_list$VS2[p] <- n
            p <- p+1
          }
        }
      }
    }
  }
}

combos_list <- combos_list[-1,]

this function builds a list of all of the combinations that the user wants to test. In this case, we’re comparing the performance of all unique combinations of the six tools.

build_score_list <- function(input_seqs, combos) {
  output <- data.frame(precision=rep(0, nrow(combos)),
                       recall=rep(0, nrow(combos)),
                       F1=rep(0, nrow(combos)),
                       MCC=rep(0, nrow(combos)),
                       AUC=rep(0, nrow(combos)),
                       fungal_FP=rep(0, nrow(combos)),
                       protist_FP=rep(0, nrow(combos)),
                       bacterial_FP=rep(0, nrow(combos)),
                       viral_FN=rep(0, nrow(combos)))
  for (i in 1:nrow(combos)) {
    keep_score <- getting_viral_set_1(input_seqs, include_vibrant = combos$VIBRANT[i],
                                            include_virsorter = combos$VS[i],
                                            include_virsorter2 = combos$VS2[i],
                                            include_tuning_viral = combos$tune_viral[i],
                                            include_tuning_not_viral = combos$tune_not_viral[i],
                                            include_deepvirfinder = combos$DVF[i])
  
    output[i,1:9] <- assess_performance(input_seqs$seqtype, keep_score)
    
    output$toolcombo[i] <- paste(combos$tune_viral[i],combos$DVF[i],
                                 combos$tune_not_viral[i], combos$VIBRANT[i],
                                 combos$VS[i], combos$VS2[i])
  }
  
  output[is.na(output)] <- 0

  return (output)
}

Calculate the performance of each pipeline

accuracy_scores <- data.frame(testing_set_index=rep(0, nrow(combos_list)*10),
                      precision=rep(0, nrow(combos_list)*10),
                       recall=rep(0, nrow(combos_list)*10),
                       F1=rep(0, nrow(combos_list)*10),
                       MCC=rep(0, nrow(combos_list)*10), 
                      AUC=rep(0, nrow(combos_list)*10),
                      fungal_FP=rep(0, nrow(combos_list)*10),
                      protist_FP=rep(0, nrow(combos_list)*10),
                      bacterial_FP=rep(0, nrow(combos_list)*10),
                      viral_FN=rep(0, nrow(combos_list)*10))

accuracy_scores <- cbind(testing_set_index=rep(1, nrow(combos_list)),
                              build_score_list(viruses[viruses$Index==1,], combos_list))
for (i in 2:10) {
  accuracy_scores <- rbind(accuracy_scores,
                           cbind(testing_set_index=rep(i, nrow(combos_list)),
                              build_score_list(viruses[viruses$Index==i,], combos_list)))
}
library("stringr")
accuracy_scores$numtools <- str_count(accuracy_scores$toolcombo, "1")
#accuracy_scores <- accuracy_scores[order(accuracy_scores$numtools, decreasing=F),]
accuracy_scores <- accuracy_scores[order(accuracy_scores$MCC, decreasing=F),]
accuracy_scores$toolcombo <- factor(accuracy_scores$toolcombo, levels = unique(accuracy_scores$toolcombo))
accuracy_scores$numtools <- as.factor(accuracy_scores$numtools)

Visualize how the precision, recall, and F1 scores change across pipelines.

pal <- ggthemes::tableau_color_pal(palette="Tableau 10", type="regular")
p2 <- ggplot(accuracy_scores, aes(x=toolcombo, y=F1, 
                                  color=numtools, fill=numtools)) +
  geom_point(alpha=0.5) +
  theme_light() +
  theme(
    panel.grid.major.y = element_blank(),
    panel.border = element_blank(),
    axis.ticks.y = element_blank(),
    legend.position = "bottom",
    axis.text.y=element_text(size=14),
    axis.text.x=element_text(size=14, angle = 90),
    legend.text=element_text(size=12),
    axis.title=element_text(size=16),
  ) +
  xlab("Tool Combination (tv, DVF, tnv, VB, VS, VS2)") +
  ylab("F1 Score")
p2

ggplot(accuracy_scores, aes(x=toolcombo, y=precision, 
                                  color=numtools, fill=numtools)) +
  geom_point(alpha=0.5) +
  theme_light() +
  theme(
    panel.grid.major.y = element_blank(),
    panel.border = element_blank(),
    axis.ticks.y = element_blank(),
    legend.position = "bottom",
    axis.text.y=element_text(size=14),
    axis.text.x=element_text(size=14, angle = 90),
    legend.text=element_text(size=12),
    axis.title=element_text(size=16),
  ) +
  xlab("Tool Combination (tv, DVF, tnv, VB, VS, VS2)") +
  ylab("Precision")

ggplot(accuracy_scores, aes(x=toolcombo, y=recall, 
                                  color=numtools, fill=numtools)) +
  geom_point(alpha=0.5) +
  theme_light() +
  theme(
    panel.grid.major.y = element_blank(),
    panel.border = element_blank(),
    axis.ticks.y = element_blank(),
    legend.position = "bottom",
    axis.text.y=element_text(size=14),
    axis.text.x=element_text(size=14, angle = 90),
    legend.text=element_text(size=12),
    axis.title=element_text(size=16),
  ) +
  xlab("Tool Combination (tv, DVF, tnv, VB, VS, VS2)") +
  ylab("Recall")

ggplot(accuracy_scores, aes(x=precision, y=recall, 
                                  color=numtools, fill=numtools)) +
  geom_point(alpha=0.5) +
  theme_light() +
  theme(
    panel.grid.major.y = element_blank(),
    panel.border = element_blank(),
    axis.ticks.y = element_blank(),
    legend.position = "bottom",
    axis.text.y=element_text(size=14),
    axis.text.x=element_text(size=14, angle = 90),
    legend.text=element_text(size=12),
    axis.title=element_text(size=16),
  ) +
  xlab("Precision") +
  ylab("Recall")

ggplot(accuracy_scores, aes(x=toolcombo, y=abs(precision-recall), 
                                  color=numtools, fill=numtools)) +
  geom_point(alpha=0.5) +
  theme_light() +
  theme(
    panel.grid.major.y = element_blank(),
    panel.border = element_blank(),
    axis.ticks.y = element_blank(),
    legend.position = "bottom",
    axis.text.y=element_text(size=14),
    axis.text.x=element_text(size=14, angle = 90),
    legend.text=element_text(size=12),
    axis.title=element_text(size=16),
  ) +
  xlab("Tool Combination (tv, DVF, tnv, VB, VS, VS2)") +
  ylab("Precision-Recall")

ggplot(accuracy_scores, aes(x=toolcombo, y=MCC, 
                                  color=numtools, fill=numtools)) +
  geom_point(alpha=0.5) +
  theme_light() +
  theme(
    panel.grid.major.y = element_blank(),
    panel.border = element_blank(),
    axis.ticks.y = element_blank(),
    legend.position = "bottom",
    axis.text.y=element_text(size=14),
    axis.text.x=element_text(size=14, angle = 90),
    legend.text=element_text(size=12),
    axis.title=element_text(size=16),
  ) +
  xlab("Tool Combination (tv, DVF, tnv, VB, VS, VS2)") +
  ylab("MCC")

ggplot(accuracy_scores, aes(x=toolcombo, y=AUC, 
                                  color=numtools, fill=numtools)) +
  geom_point(alpha=0.5) +
  theme_light() +
  theme(
    panel.grid.major.y = element_blank(),
    panel.border = element_blank(),
    axis.ticks.y = element_blank(),
    legend.position = "bottom",
    axis.text.y=element_text(size=14),
    axis.text.x=element_text(size=14, angle = 90),
    legend.text=element_text(size=12),
    axis.title=element_text(size=16),
  ) +
  xlab("Tool Combination (tv, DVF, tnv, VB, VS, VS2)") +
  ylab("AUC")

ggplot(accuracy_scores, aes(x=toolcombo, y=fungal_FP, 
                                  color=numtools, fill=numtools)) +
  geom_point(alpha=0.5) +
  theme_light() +
  theme(
    panel.grid.major.y = element_blank(),
    panel.border = element_blank(),
    axis.ticks.y = element_blank(),
    legend.position = "bottom",
    axis.text.y=element_text(size=14),
    axis.text.x=element_text(size=14, angle = 90),
    legend.text=element_text(size=12),
    axis.title=element_text(size=16),
  ) +
  xlab("Tool Combination (tv, DVF, tnv, VB, VS, VS2)") +
  ylab("Fungal False Positives")


ggplot(accuracy_scores, aes(x=toolcombo, y=protist_FP, 
                                  color=numtools, fill=numtools)) +
  geom_point(alpha=0.5) +
  theme_light() +
  theme(
    panel.grid.major.y = element_blank(),
    panel.border = element_blank(),
    axis.ticks.y = element_blank(),
    legend.position = "bottom",
    axis.text.y=element_text(size=14),
    axis.text.x=element_text(size=14, angle = 90),
    legend.text=element_text(size=12),
    axis.title=element_text(size=16),
  ) +
  xlab("Tool Combination (tv, DVF, tnv, VB, VS, VS2)") +
  ylab("Protist False Positives")


ggplot(accuracy_scores, aes(x=toolcombo, y=bacterial_FP, 
                                  color=numtools, fill=numtools)) +
  geom_point(alpha=0.5) +
  theme_light() +
  theme(
    panel.grid.major.y = element_blank(),
    panel.border = element_blank(),
    axis.ticks.y = element_blank(),
    legend.position = "bottom",
    axis.text.y=element_text(size=14),
    axis.text.x=element_text(size=14, angle = 90),
    legend.text=element_text(size=12),
    axis.title=element_text(size=16),
  ) +
  xlab("Tool Combination (tv, DVF, tnv, VB, VS, VS2)") +
  ylab("Bacterial False Positives")

ggplot(accuracy_scores, aes(x=toolcombo, y=viral_FN, 
                                  color=numtools, fill=numtools)) +
  geom_point(alpha=0.5) +
  theme_light() +
  theme(
    panel.grid.major.y = element_blank(),
    panel.border = element_blank(),
    axis.ticks.y = element_blank(),
    legend.position = "bottom",
    axis.text.y=element_text(size=14),
    axis.text.x=element_text(size=14, angle = 90),
    legend.text=element_text(size=12),
    axis.title=element_text(size=16),
  ) +
  xlab("Tool Combination (tv, DVF, tnv, VB, VS, VS2)") +
  ylab("Viral False Negatives")

write_tsv(accuracy_scores, "20221029_accuracy_scores.tsv")

to do: add in clustering and ordination like in the drinking water R notebook

Experimenting

high precision example

viruses$keep_score_high_precision <- getting_viral_set_1(viruses, include_deepvirfinder = T,
                                              include_vibrant = T,
                                              include_virsorter2 = F,
                                              include_tuning_viral = F,
                                              include_tuning_not_viral = T,
                                              include_virsorter = F)
viruses$confusion_matrix_high_precision <- "true negative"
viruses$confusion_matrix_high_precision[viruses$seqtype=="virus" & viruses$keep_score_high_precision<1] <- "false negative"
viruses$confusion_matrix_high_precision[viruses$seqtype=="virus" & viruses$keep_score_high_precision>=1] <- "true positive"
viruses$confusion_matrix_high_precision[viruses$seqtype!="virus" & viruses$keep_score_high_precision>=1] <- "false positive"

visualizing confusion matrix by taxa

confusion_by_taxa <- melt(table(viruses$confusion_matrix_high_precision, viruses$seqtype, viruses$Index))
The melt generic in data.table has been passed a table and will attempt to redirect to the relevant reshape2 method; please note that reshape2 is deprecated, and this redirection is now deprecated as well. To continue using melt methods from reshape2 while both libraries are attached, e.g. melt.list, you can prepend the namespace like reshape2::melt(table(viruses$confusion_matrix_high_precision, viruses$seqtype,     viruses$Index)). In the next version, this warning will become an error.
colnames(confusion_by_taxa) <- c("confusion_matrix", "seqtype","Index", "count")
length(grep("true", viruses$confusion_matrix_high_precision))/nrow(viruses)
[1] 0.9206364
pal <- ggthemes::tableau_color_pal(palette="Tableau 10", type="regular")
ggplot(confusion_by_taxa, aes(x=count, y=as.factor(Index),
                   fill=confusion_matrix,
                   color=confusion_matrix)) +
  geom_bar(stat="identity") +
  theme_light() +
  theme(
    panel.grid.major.y = element_blank(),
    panel.border = element_blank(),
    axis.ticks.y = element_blank(),
    legend.position = "bottom",
    axis.text.y=element_text(size=14),
    axis.text.x=element_text(size=14),
    legend.text=element_text(size=12),
    axis.title=element_text(size=16),
  ) +
  scale_fill_manual(name="",
                     values = alpha(rev(pal(4)), 0.5),
                    labels=c("false negative", "false positive", 
                             "true negative", "true positive")) +
  scale_color_manual(name="",
                     values = alpha(rev(pal(4)), 1),
                    labels=c("false negative", "false positive", 
                             "true negative", "true positive")) +
  xlab("Number of Sequences") +
  ylab("") + 
  facet_wrap(~seqtype, scales = "free") +
  coord_flip()

this rule set had the highest precision, but as you can see, this comes with a big sacrifice in recall

high MCC example

viruses$keep_score_high_MCC <- getting_viral_set_1(viruses, include_deepvirfinder = F,
                                              include_vibrant = T,
                                              include_virsorter2 = T,
                                              include_tuning_viral = T,
                                              include_tuning_not_viral = T,
                                              include_virsorter = T)
viruses$confusion_matrix_high_MCC <- "true negative"
viruses$confusion_matrix_high_MCC[viruses$seqtype=="virus" & viruses$keep_score_high_MCC<1] <- "false negative"
viruses$confusion_matrix_high_MCC[viruses$seqtype=="virus" & viruses$keep_score_high_MCC>=1] <- "true positive"
viruses$confusion_matrix_high_MCC[viruses$seqtype!="virus" & viruses$keep_score_high_MCC>=1] <- "false positive"

accuracy:

length(grep("true", viruses$confusion_matrix_high_MCC))/nrow(viruses)
[1] 0.9422999

recall

length(grep("true positive", viruses$confusion_matrix_high_MCC))/length(grep("virus", viruses$seqtype))
[1] 0.9039
viruses$size_class <- "3-10kb"
viruses$size_class[viruses$checkv_length>10000] <- ">10kb"

visualizing confusion matrix by taxa

confusion_by_taxa <- viruses %>% count(confusion_matrix_high_MCC, seqtype, Index)

colnames(confusion_by_taxa) <- c("confusion_matrix", "seqtype","index", "count")
pal <- ggthemes::tableau_color_pal(palette="Tableau 10", type="regular")
ggplot(confusion_by_taxa, aes(x=count, y=as.factor(index),
                   fill=confusion_matrix,
                   color=confusion_matrix)) +
  geom_bar(stat="identity") +
  theme_light() +
  theme(
    panel.grid.major.y = element_blank(),
    panel.border = element_blank(),
    axis.ticks.y = element_blank(),
    legend.position = "bottom",
    axis.text.y=element_text(size=14),
    axis.text.x=element_text(size=14),
    legend.text=element_text(size=12),
    axis.title=element_text(size=16),
  ) +
  scale_fill_manual(name="",
                     values = alpha(rev(pal(4)), 0.5),
                    labels=c("false negative", "false positive", 
                             "true negative", "true positive")) +
  scale_color_manual(name="",
                     values = alpha(rev(pal(4)), 1),
                    labels=c("false negative", "false positive", 
                             "true negative", "true positive")) +
  xlab("Number of Sequences") +
  ylab("") + 
  facet_wrap(~seqtype, scales = "free") +
  coord_flip()

percent viral

visualizing confusion matrix by taxa

confusion_by_taxa <- viruses %>% count(confusion_matrix_high_MCC, seqtype, size_class, Index)

colnames(confusion_by_taxa) <- c("confusion_matrix", "seqtype","size", "index", "count")
confusion_vir_called <- confusion_by_taxa %>% filter(confusion_matrix=="true positive" | confusion_matrix=="false positive") 

type_count <- viruses %>% count(seqtype, size_class, Index)

confusion_vir_called$per_viral <- 0

for (i in c(1:nrow(confusion_vir_called))) {
  confusion_vir_called$per_viral[i] <- confusion_vir_called$count[i]/type_count$n[type_count$seqtype==confusion_vir_called$seqtype[i] & 
                                                                                    type_count$Index==confusion_vir_called$index[i] &
                                                                                    type_count$size_class==confusion_vir_called$size[i]]*100
}

confusion_vir_called <- confusion_vir_called %>% group_by(seqtype, size) %>%
  summarise(mean=mean(per_viral), 
            sd=sd(per_viral))
`summarise()` has grouped output by 'seqtype'. You can override using the `.groups` argument.
ggplot(confusion_vir_called, aes(y=mean, x=size,
                   fill=seqtype,
                   color=seqtype)) +
  geom_bar(stat="identity", position=position_dodge()) +
  theme_light() +
  theme(
    panel.grid.major.y = element_blank(),
    panel.border = element_blank(),
    axis.ticks.y = element_blank(),
    legend.position = "bottom",
    axis.text.y=element_text(size=14),
    axis.text.x=element_text(size=14),
    legend.text=element_text(size=12),
    axis.title=element_text(size=16),
  ) +
  geom_errorbar(aes(ymin=mean-sd, ymax=mean+sd), width=.2,
                 position=position_dodge(.9)) +
  scale_fill_manual(name="",
                     values = alpha(rev(pal(6)), 0.5)) +
  scale_color_manual(name="",
                     values = alpha(rev(pal(6)), 1)) +
  xlab("Length (bp)") +
  ylab("Sequences Called Viral (%)") 

viruses$keep_score_vb <- getting_viral_set_1(viruses, include_deepvirfinder = F,
                                              include_vibrant = T,
                                              include_virsorter2 = F,
                                              include_tuning_viral = F,
                                              include_tuning_not_viral = F,
                                              include_virsorter = F)

viruses$keep_score_vb_dvf <- getting_viral_set_1(viruses, include_deepvirfinder = T,
                                              include_vibrant = T,
                                              include_virsorter2 = F,
                                              include_tuning_viral = F,
                                              include_tuning_not_viral = F,
                                              include_virsorter = F)

viruses$keep_score_vb_dvf_vs2 <- getting_viral_set_1(viruses, include_deepvirfinder = T,
                                              include_vibrant = T,
                                              include_virsorter2 = T,
                                              include_tuning_viral = F,
                                              include_tuning_not_viral = F,
                                              include_virsorter = F)

viruses$keep_score_vb_dvf_vs2_vs <- getting_viral_set_1(viruses, include_deepvirfinder = T,
                                              include_vibrant = T,
                                              include_virsorter2 = T,
                                              include_tuning_viral = F,
                                              include_tuning_not_viral = F,
                                              include_virsorter = T)

viruses$keep_score_vb_dvf_vs2_vs_tv <- getting_viral_set_1(viruses, include_deepvirfinder = T,
                                              include_vibrant = T,
                                              include_virsorter2 = T,
                                              include_tuning_viral = T,
                                              include_tuning_not_viral = F,
                                              include_virsorter = T)

viruses$keep_score_vb_dvf_vs2_vs_tv_tnv <- getting_viral_set_1(viruses, include_deepvirfinder = T,
                                              include_vibrant = T,
                                              include_virsorter2 = T,
                                              include_tuning_viral = T,
                                              include_tuning_not_viral = T,
                                              include_virsorter = T)

Considering how each method contributes to the final prediction

viruses_high <- viruses[viruses$keep_score_vb_dvf_vs2_vs_tv>=1,] 
viruses_high_mod <- viruses_high %>% select(keep_score_vb,keep_score_vb_dvf, 
                                            keep_score_vb_dvf_vs2, keep_score_vb_dvf_vs2_vs, 
                                            keep_score_vb_dvf_vs2_vs_tv, keep_score_vb_dvf_vs2_vs_tv_tnv)
#viruses_high_mod <- apply(viruses_high_mod, c(1,2), function(x) {if (x >= 1) {x <- 1} else {x <- 0}})
viruses_high_mod <- as_tibble(viruses_high_mod)
sm_m <- reshape2::melt(viruses_high_mod)
No id variables; using all as measure variables
colnames(sm_m) <- c("method", "viral_score")
sm_m <- sm_m[sm_m$viral_score>0,]

sm_m$score <- sm_m$viral_score

sm_m$score[sm_m$viral_score==0.5] <- "0.5"
sm_m$score[sm_m$viral_score>=1] <- "1"
sm_m$score[sm_m$viral_score>=2] <- "2"
sm_m$score[sm_m$viral_score>=3] <- "3"
sm_m$score[sm_m$viral_score>=4] <- "4"
sm_m$score[sm_m$viral_score>=5] <- "5"

sm_m$score <- factor(sm_m$score, 
                                       levels=c("0.5", "1", "2","3","4","5"))
ggplot(sm_m, aes(x=method, y=score,
                   fill=score)) +
  geom_bar(stat="identity") +
  theme_light() +
  coord_flip() +
  theme(
    panel.grid.major.y = element_blank(),
    panel.border = element_blank(),
    legend.position = "bottom"
  ) +
  scale_fill_manual(name = 'Viral Score',
                     values = alpha(c(viridis(6)), 1)) +
  xlab("") +
  ylab("Number of Sequences") +
  coord_flip()
Coordinate system already present. Adding new coordinate system, which will replace the existing one.

Visualizing confusion matrix by number of tools

viruses$keep_score_visualize <- viruses$keep_score_high_MCC
viruses$keep_score_visualize[viruses$keep_score_high_MCC>1] <- "> 1"
viruses$keep_score_visualize[viruses$keep_score_high_MCC==1] <- "1"
viruses$keep_score_visualize[viruses$keep_score_high_MCC==0.5] <- "0.5"
viruses$keep_score_visualize[viruses$keep_score_high_MCC==0] <- "0"
viruses$keep_score_visualize[viruses$keep_score_high_MCC==-0.5] <- "-0.5"
viruses$keep_score_visualize[viruses$keep_score_high_MCC==-1] <- "-1"
viruses$keep_score_visualize[viruses$keep_score_high_MCC<=-1] <- "< -1"

viruses$keep_score_visualize <- factor(viruses$keep_score_visualize, 
                                       levels=c("< -1", "-1", "-0.5", "0", "0.5","1", "> 1"))
#viruses$keep_score_visualize <- factor(viruses$keep_score_visualize, 
#                                       labels=c("≤ 0", "≤ 0", "≤ 0", "0.5","1", "> 1"))
levels(factor(viruses$keep_score_visualize))
[1] "< -1" "-0.5" "0"    "0.5"  "1"    "> 1" 
ggplot(viruses, aes(x=as.factor(Index),
                   fill=keep_score_visualize, color=keep_score_visualize)) +
  geom_bar(stat="count", position="stack") +
  theme_light() +
  coord_flip() +
  theme(
    panel.grid.major.y = element_blank(),
    panel.border = element_blank(),
    axis.ticks.y = element_blank(),
    legend.position = "bottom",
    axis.text.y=element_text(size=14),
    axis.text.x=element_text(size=14),
    legend.text=element_text(size=12),
    axis.title=element_text(size=16)
  ) +
  scale_color_manual(name = 'Viral Score',
                     values = alpha(c(viridis(6)), 1)) +
  scale_fill_manual(name = 'Viral Score',
                     values = alpha(c(viridis(6)), 0.5)) +
  xlab("Index") +
  ylab("Sequence Count") +
  facet_wrap(~confusion_matrix_high_MCC, scales = "free")

all 6 tools example

viruses$keep_score_all <- getting_viral_set_1(viruses, include_deepvirfinder = T,
                                              include_vibrant = T,
                                              include_virsorter2 = T,
                                              include_tuning_viral = T,
                                              include_tuning_not_viral = T,
                                              include_virsorter = T)
viruses$confusion_matrix_all <- "true negative"
viruses$confusion_matrix_all[viruses$seqtype=="virus" & viruses$keep_score_all<1] <- "false negative"
viruses$confusion_matrix_all[viruses$seqtype=="virus" & viruses$keep_score_all>=1] <- "true positive"
viruses$confusion_matrix_all[viruses$seqtype!="virus" & viruses$keep_score_all>=1] <- "false positive"

visualizing confusion matrix by taxa

confusion_by_taxa <- melt(table(viruses$confusion_matrix_all, viruses$seqtype, viruses$Index))
The melt generic in data.table has been passed a table and will attempt to redirect to the relevant reshape2 method; please note that reshape2 is deprecated, and this redirection is now deprecated as well. To continue using melt methods from reshape2 while both libraries are attached, e.g. melt.list, you can prepend the namespace like reshape2::melt(table(viruses$confusion_matrix_all, viruses$seqtype, viruses$Index)). In the next version, this warning will become an error.
colnames(confusion_by_taxa) <- c("confusion_matrix", "seqtype","Index", "count")
table(viruses$confusion_matrix_all)

false negative false positive  true negative  true positive 
           786           5508          80783           9214 
length(grep("true", viruses$confusion_matrix_all))/nrow(viruses)
[1] 0.9346356
length(grep("true positive", viruses$confusion_matrix_all))/length(grep("virus", viruses$seqtype))
[1] 0.9214
pal <- ggthemes::tableau_color_pal(palette="Tableau 10", type="regular")
ggplot(confusion_by_taxa, aes(x=count, y=as.factor(Index),
                   fill=confusion_matrix,
                   color=confusion_matrix)) +
  geom_bar(stat="identity") +
  theme_light() +
  theme(
    panel.grid.major.y = element_blank(),
    panel.border = element_blank(),
    axis.ticks.y = element_blank(),
    legend.position = "bottom",
    axis.text.y=element_text(size=14),
    axis.text.x=element_text(size=14),
    legend.text=element_text(size=12),
    axis.title=element_text(size=16),
  ) +
  scale_fill_manual(name="",
                     values = alpha(rev(pal(4)), 0.5),
                    labels=c("false negative", "false positive", 
                             "true negative", "true positive")) +
  scale_color_manual(name="",
                     values = alpha(rev(pal(4)), 1),
                    labels=c("false negative", "false positive", 
                             "true negative", "true positive")) +
  xlab("Number of Sequences") +
  ylab("") + 
  facet_wrap(~seqtype, scales = "free") +
  coord_flip()

high recall example

viruses$keep_score_high_recall <- getting_viral_set_1(viruses, include_deepvirfinder = T,
                                              include_vibrant = T,
                                              include_virsorter2 = T,
                                              include_tuning_viral = T,
                                              include_tuning_not_viral = F,
                                              include_virsorter = T)
viruses$confusion_matrix_high_recall <- "true negative"
viruses$confusion_matrix_high_recall[viruses$seqtype=="virus" & viruses$keep_score_high_recall<1] <- "false negative"
viruses$confusion_matrix_high_recall[viruses$seqtype=="virus" & viruses$keep_score_high_recall>=1] <- "true positive"
viruses$confusion_matrix_high_recall[viruses$seqtype!="virus" & viruses$keep_score_high_recall>=1] <- "false positive"

visualizing confusion matrix by taxa

confusion_by_taxa <- melt(table(viruses$confusion_matrix_high_recall, viruses$seqtype, viruses$Index))
The melt generic in data.table has been passed a table and will attempt to redirect to the relevant reshape2 method; please note that reshape2 is deprecated, and this redirection is now deprecated as well. To continue using melt methods from reshape2 while both libraries are attached, e.g. melt.list, you can prepend the namespace like reshape2::melt(table(viruses$confusion_matrix_high_recall, viruses$seqtype,     viruses$Index)). In the next version, this warning will become an error.
colnames(confusion_by_taxa) <- c("confusion_matrix", "seqtype","Index", "count")
pal <- ggthemes::tableau_color_pal(palette="Tableau 10", type="regular")
p2 <- ggplot(confusion_by_taxa, aes(x=count, y=as.factor(Index),
                   fill=confusion_matrix,
                   color=confusion_matrix)) +
  geom_bar(stat="identity") +
  theme_light() +
  theme(
    panel.grid.major.y = element_blank(),
    panel.border = element_blank(),
    axis.ticks.y = element_blank(),
    legend.position = "bottom",
    axis.text.y=element_text(size=14),
    axis.text.x=element_text(size=14),
    legend.text=element_text(size=12),
    axis.title=element_text(size=16),
  ) +
  scale_fill_manual(name="",
                     values = alpha(rev(pal(4)), 0.5),
                    labels=c("false negative", "false positive", 
                             "true negative", "true positive")) +
  scale_color_manual(name="",
                     values = alpha(rev(pal(4)), 1),
                    labels=c("false negative", "false positive", 
                             "true negative", "true positive")) +
  xlab("Number of Sequences") +
  ylab("") + 
  facet_wrap(~seqtype, scales = "free") +
  coord_flip()
p2

accuracy:

length(grep("true", viruses$confusion_matrix_high_recall))/nrow(viruses)
[1] 0.8834574

0.887

recall

length(grep("true positive", viruses$confusion_matrix_high_recall))/length(grep("virus", viruses$seqtype))
[1] 0.9604

recover almost all of the viruses this way, but more protist contamination

0.960

Extra Stuff #####################################################################

ggplot(viruses, aes(x=checkv_length, y=keep_score_high_MCC,
                   fill=confusion_matrix_high_MCC,
                   color=confusion_matrix_high_MCC)) +
  geom_point(stat="identity", shape=21) +
  theme_light() +
  theme(
    panel.grid.major.y = element_blank(),
    panel.border = element_blank(),
    axis.ticks.y = element_blank(),
    legend.position = "bottom",
    axis.text.y=element_text(size=14),
    axis.text.x=element_text(size=14),
    legend.text=element_text(size=12),
    axis.title=element_text(size=16),
  ) +
  scale_fill_manual(name="",
                     values = alpha(rev(pal(4)), 0.5),
                    labels=c("false negative", "false positive", 
                             "true negative", "true positive")) +
  scale_color_manual(name="",
                     values = alpha(rev(pal(4)), 1),
                    labels=c("false negative", "false positive", 
                             "true negative", "true positive")) +
  xlab("Sequence Length (bp)") +
  ylab("Pipeline Viral Score") + 
  facet_wrap(~seqtype) + 
  scale_x_log10()

ggplot(viruses, aes(x=checkv_completeness, y=hallmark,
                   fill=confusion_matrix_high_recall,
                   color=confusion_matrix_high_recall)) +
  geom_point(stat="identity", shape=21) +
  theme_light() +
  theme(
    panel.grid.major.y = element_blank(),
    panel.border = element_blank(),
    axis.ticks.y = element_blank(),
    legend.position = "bottom",
    axis.text.y=element_text(size=14),
    axis.text.x=element_text(size=14),
    legend.text=element_text(size=12),
    axis.title=element_text(size=16),
  ) +
  scale_fill_manual(name="",
                     values = alpha(rev(pal(4)), 0.5),
                    labels=c("false negative", "false positive", 
                             "true negative", "true positive")) +
  scale_color_manual(name="",
                     values = alpha(rev(pal(4)), 1),
                    labels=c("false negative", "false positive", 
                             "true negative", "true positive")) +
  xlab("CheckV Completeness") +
  ylab("Number of Hallmark Genes") + 
  facet_wrap(~seqtype) + 
  scale_x_log10()

ggplot(viruses, aes(x=checkv_completeness, y=keep_score_high_MCC,
                   fill=confusion_matrix_high_recall,
                   color=confusion_matrix_high_recall)) +
  geom_point(stat="identity", shape=21) +
  theme_light() +
  theme(
    panel.grid.major.y = element_blank(),
    panel.border = element_blank(),
    axis.ticks.y = element_blank(),
    legend.position = "bottom",
    axis.text.y=element_text(size=14),
    axis.text.x=element_text(size=14),
    legend.text=element_text(size=12),
    axis.title=element_text(size=16),
  ) +
  scale_fill_manual(name="",
                     values = alpha(rev(pal(4)), 0.5),
                    labels=c("false negative", "false positive", 
                             "true negative", "true positive")) +
  scale_color_manual(name="",
                     values = alpha(rev(pal(4)), 1),
                    labels=c("false negative", "false positive", 
                             "true negative", "true positive")) +
  xlab("CheckV Completeness") +
  ylab("Pipeline Viral Score") + 
  facet_wrap(~seqtype) + 
  scale_x_log10()

ggplot(viruses, aes(x=confusion_matrix_high_recall, y=checkv_length,
                   fill=confusion_matrix_high_recall,
                   color=confusion_matrix_high_recall)) +
  geom_boxplot() +
  theme_light() +
  theme(
    panel.grid.major.y = element_blank(),
    panel.border = element_blank(),
    axis.ticks.y = element_blank(),
    legend.position = "bottom",
    axis.text.y=element_text(size=14),
    axis.text.x=element_text(size=14),
    legend.text=element_text(size=12),
    axis.title=element_text(size=16),
  ) +
  scale_fill_manual(name="",
                     values = alpha(rev(pal(4)), 0.5),
                    labels=c("false negative", "false positive", 
                             "true negative", "true positive")) +
  scale_color_manual(name="",
                     values = alpha(rev(pal(4)), 1),
                    labels=c("false negative", "false positive", 
                             "true negative", "true positive")) +
  xlab("Sequence Length (bp)") +
  ylab("Pipeline Viral Score") +
  scale_y_log10()

looking at false negatives

viruses_false_negs <- viruses[(viruses$seqtype=="virus" & viruses$keep_score_high_recall<1),]

looking at protists calling viral

viruses_false_pos_protists <- viruses[(viruses$seqtype=="protist" & viruses$keep_score_high_recall>=1),]

Considering how each method contributes to the final prediction (high MCC)

viruses$keep_score_vb <- getting_viral_set_1(viruses, include_deepvirfinder = F,
                                              include_vibrant = T,
                                              include_virsorter2 = F,
                                              include_tuning_viral = F,
                                              include_tuning_not_viral = F,
                                              include_virsorter = F)

viruses$keep_score_vb_tv <- getting_viral_set_1(viruses, include_deepvirfinder = F,
                                              include_vibrant = T,
                                              include_virsorter2 = T,
                                              include_tuning_viral = T,
                                              include_tuning_not_viral = F,
                                              include_virsorter = F)
viruses_high <- viruses[viruses$keep_score_vb_tv>=1,] #uncomment this line if want to use all 6 tools
viruses_high_mod <- viruses_high %>% select(keep_score_vb, 
                                            keep_score_vb_tv)
#viruses_high_mod <- apply(viruses_high_mod, c(1,2), function(x) {if (x >= 1) {x <- 1} else {x <- 0}})
viruses_high_mod <- as_tibble(viruses_high_mod)
sm_m <- reshape2::melt(viruses_high_mod)
No id variables; using all as measure variables
colnames(sm_m) <- c("method", "score")
ggplot(sm_m, aes(x=method, y=score,
                   fill=as.factor(score))) +
  geom_bar(stat="identity") +
  theme_light() +
  coord_flip() +
  theme(
    panel.grid.major.y = element_blank(),
    panel.border = element_blank(),
    axis.ticks.y = element_blank(),
    legend.position = "bottom"
  ) +
  scale_fill_manual(name = 'Number of Methods',
                     values = alpha(c(viridis(14)), 1)) +
  xlab("Primary Method") +
  ylab("Count of Viral Contigs") +
  coord_flip()
Coordinate system already present. Adding new coordinate system, which will replace the existing one.

ROC

library(pROC)
viruses$truepositive <- rep(0, nrow(viruses))
viruses$truepositive[viruses$seqtype=="virus"] <- 1
rocobj <- roc(viruses$truepositive, viruses$keep_score)
Unknown or uninitialised column: `keep_score`.Error in roc.default(viruses$truepositive, viruses$keep_score) : 
  No valid data provided.

Sensitivity: The probability that the model predicts a positive outcome for an observation when indeed the outcome is positive. Specificity: The probability that the model predicts a negative outcome for an observation when indeed the outcome is negative.

Comparing behavior of all testing sets combined (clustering analyses)

to do: try coloring above based on the F1 scores of the testing set on each combination

LS0tCnRpdGxlOiAiVmlyYWwgU2VxdWVuY2UgU29ydGluZyBUb29scyBFdmFsdWF0aW9uIgphdXRob3I6IEJyaWRnZXQgSGVnYXJ0eSwgSmFtZXMgUmlkZGVsbApkYXRlOiAwNy0yMi0yMDIyCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KVGhpcyBSbWFya2Rvd24gZmlsZSBhc3Nlc3NlcyB0aGUgb3V0cHV0IG9mIENoZWNrViwgRGVlcFZpckZpbmRlciwgS2FpanUsClZJQlJBTlQsIFZpclNvcnRlciwgYW5kIFZpclNvcnRlcjIgb24gbXVsdGlwbGUgdHJhaW5pbmcgc2V0cyBvZiBtaWNyb2JpYWwgRE5BLCAKcHJpbWFyaWx5IGZyb20gTkNCSS4gQ3JlYXRlZCBmcm9tIGZ1bmdhbCwgdmlyYWwsIGJhY3RlcmlhbCwgYXJjaGVhZWwsIHByb3Rpc3QsCmFuZCBwbGFzbWlkIEROQSBzZXF1ZW5jZXMKClBsZWFzZSByZWFjaCBvdXQgdG8gSmFtZXMgUmlkZGVsbCAocmlkZGVsbC4yNkBidWNrZXllbWFpbC5vc3UuZWR1KSBvcgpCcmlkZ2V0IEhlZ2FydHkgKGJlaDUzQGNhc2UuZWR1KSByZWdhcmRpbmcgYW55IGlzc3Vlcywgb3Igb3BlbiBhbiBpc3N1ZSBvbiBnaXRodWIuCgpgYGB7ciBzZXR1cC1saWJyYXJ5fQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkocGx5cikKbGlicmFyeShyZXNoYXBlMikKbGlicmFyeSh2aXJpZGlzKQpsaWJyYXJ5KHRpZHlyKQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KHJlYWRyKQpsaWJyYXJ5KGRhdGEudGFibGUpCmxpYnJhcnkocFJPQykKYGBgCgpJbXBvcnQgdGhlIGZpbGUgdGhhdCBjb21iaW5lcyB0aGUgcmVzdWx0cyBmcm9tIGVhY2ggb2YgdGhlIHRvb2xzIGZyb20gcnVubmluZyAiY29tYmluaW5nX3Rvb2xfb3V0cHV0LlJtZCI6CmBgYHtyfQp2aXJ1c2VzIDwtIHJlYWRfdHN2KCIuLi9JbnRlcm1lZGlhcnlGaWxlcy92aXJhbF90b29sc19jb21iaW5lZC50c3YiKQpgYGAKCgoKClRoaXMgc2VjdGlvbiBkZWZpbmVzIGEgdmlyYWxuZXNzIHNjb3JlICJrZWVwX3Njb3JlIiBiYXNlZCBvbiB0aGUgdG9vbCBjbGFzc2lmaWNhdGlvbnMuIApBIGZpbmFsIGtlZXBfc2NvcmUgYWJvdmUgMSBpbmRpY2F0ZXMgd2Ugd2lsbCBrZWVwIHRoYXQgc2VxdWVuY2UgYW5kIGNhbGwgaXQgdmlyYWwuCgpWSUJSQU5UCiAgICBRdWFsaXR5ID09ICJDb21wbGV0ZSBDaXJjdWxhciI6ICsxCiAgICBRdWFsaXR5ID09ICJIaWdoIFF1YWxpdHkgRHJhZnQiOiArMQogICAgUXVhbGl0eSA9PSAiTWVkaXVtIFF1YWxpdHkgRHJhZnQiOiArMQogICAgUXVhbGl0eSA9PSAiTG93IFF1YWxpdHkgRHJhZnQiICYgcHJvdmlydXM6ICswLjUKClZpcnNvcnRlcjIKICAgIFZpcmFsID49IDUwOiArMC41CiAgICBWaXJhbCA+PSAwLjk1OiArMC41CiAgICBSTkEgPj0gMC45OiArMQogICAgbGF2aWRhdmlyaWRhZSA+PSAwLjk6ICsxCiAgICBOQ0xEViA+PSAwLjk6ICsxCgpWaXJzb3J0ZXIKICAgIGNhdGVnb3J5ID09ICAxLDQ6ICsxCiAgICBjYXRlZ29yeSA9PSAyLDU6ICswLjUKCkRlZXBWaXJGaW5kZXI6CiAgICBTY29yZSA+PSAwLjc6ICswLjUKClR1bmluZyAtIE5vIFZpcmFsIFNpZ25hdHVyZToKICAgIEthaWp1X3ZpcmFsID0gImNlbGx1bGFyIG9yZ2FuaXNtcyI6IC0wLjUKICAgIElmIGhvc3RfZ2VuZXMgPjUwIGFuZCBOT1QgcHJvdmlydXM6IC0xIAogICAgSWYgdmlyYWxfZ2VuZXMgPT0gMCBhbmQgaG9zdF9nZW5lcyA+PSAxOiAtMQogICAgSWYgMyp2aXJhbF9nZW5lcyA8PSBob3N0X2dlbmVzIGFuZCBOT1QgcHJvdmlydXM6IC0xCiAgICBJZiBsZW5ndGggPiA1MCwwMDAgYW5kIGhhbGxtYXJrIDw9MTogLTEKICAgIElmIGxlbmd0aCA8IDUwMDAgYW5kIGNoZWNrdiBjb21wbGV0ZW5lc3MgPD0gNzU6IC0wLjUKClR1bmluZyAtIFZpcmFsIFNpZ25hdHVyZToKICAgIEthaWp1X3ZpcmFsID0gIlZpcnVzZXMiOiArMQogICAgSWYgJXVua25vd24gPj0gNzUgYW5kIGxlbmd0aCA8IDUwMDAwOiArMC41CiAgICBJZiAldmlyYWwgPj0gNTA6ICswLjUKICAgIEhhbGxtYXJrID4gMjogKzEKICAgIAoKVGhpcyBzY3JpcHQgcHJvZHVjZXMgdmlzdWFsaXphdGlvbnMgb2YgdGhlc2UgY29tYmluZWQgdmlyYWwgc2NvcmluZ3MgYW5kCmluY2x1ZGVzIGVjb2xvZ2ljYWwgbWV0cmljcyBsaWtlIGFscGhhIGRpdmVyc2l0eS4KCllvdSBjYW4gZGVjaWRlIHdoaWNoIGNvbWJpbmF0aW9uIGlzIGFwcHJvcHJpYXRlIGZvciB0aGVtIGFuZCBvbmx5IG5lZWQgdXNlIHRoZQp0b29scyBhcHByb3ByaWF0ZSBmb3IgeW91ciBkYXRhLgoKYGBge3IgZ2V0dGluZ192aXJhbF9zZXRfMX0KZ2V0dGluZ192aXJhbF9zZXRfMSA8LSBmdW5jdGlvbihpbnB1dF9zZXFzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGluY2x1ZGVfdmlicmFudD1GQUxTRSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaW5jbHVkZV92aXJzb3J0ZXIyPUZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGluY2x1ZGVfZGVlcHZpcmZpbmRlcj1GQUxTRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpbmNsdWRlX3R1bmluZ192aXJhbD1GQUxTRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpbmNsdWRlX3R1bmluZ19ub3RfdmlyYWw9RkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaW5jbHVkZV92aXJzb3J0ZXI9RkFMU0UpIHsKICAKICBrZWVwX3Njb3JlIDwtIHJlcCgwLCBucm93KGlucHV0X3NlcXMpKQogIAogIGlmIChpbmNsdWRlX3ZpYnJhbnQpIHsKICAgIGtlZXBfc2NvcmVbaW5wdXRfc2VxcyR2aWJyYW50X3F1YWxpdHk9PSJjb21wbGV0ZSBjaXJjdWxhciJdIDwtIGtlZXBfc2NvcmVbaW5wdXRfc2VxcyR2aWJyYW50X3F1YWxpdHk9PSJjb21wbGV0ZSBjaXJjdWxhciJdICsgMQogICAga2VlcF9zY29yZVtpbnB1dF9zZXFzJHZpYnJhbnRfcXVhbGl0eT09ImhpZ2ggcXVhbGl0eSBkcmFmdCJdIDwtIGtlZXBfc2NvcmVbaW5wdXRfc2VxcyR2aWJyYW50X3F1YWxpdHk9PSJoaWdoIHF1YWxpdHkgZHJhZnQiXSArIDEKICAgIGtlZXBfc2NvcmVbaW5wdXRfc2VxcyR2aWJyYW50X3F1YWxpdHk9PSJtZWRpdW0gcXVhbGl0eSBkcmFmdCJdIDwtIGtlZXBfc2NvcmVbaW5wdXRfc2VxcyR2aWJyYW50X3F1YWxpdHk9PSJtZWRpdW0gcXVhbGl0eSBkcmFmdCJdICsgMQogICAga2VlcF9zY29yZVtpbnB1dF9zZXFzJHZpYnJhbnRfcXVhbGl0eT09ImxvdyBxdWFsaXR5IGRyYWZ0IiAmIGlucHV0X3NlcXMkcHJvdmlydXNdIDwtIGtlZXBfc2NvcmVbaW5wdXRfc2VxcyR2aWJyYW50X3F1YWxpdHk9PSJsb3cgcXVhbGl0eSBkcmFmdCIgJiBpbnB1dF9zZXFzJHByb3ZpcnVzXSArIDAuNQogIH0KICAKICBpZiAoaW5jbHVkZV92aXJzb3J0ZXIyKSB7CiAgICBrZWVwX3Njb3JlW2lucHV0X3NlcXMkdmlyYWw+PTUwXSA8LSBrZWVwX3Njb3JlW2lucHV0X3NlcXMkdmlyYWw+PTUwXSArIDAuNQogICAga2VlcF9zY29yZVtpbnB1dF9zZXFzJHZpcmFsPj05NV0gPC0ga2VlcF9zY29yZVtpbnB1dF9zZXFzJHZpcmFsPj05NV0gKyAwLjUKICAgIGtlZXBfc2NvcmVbaW5wdXRfc2VxcyRSTkE+PTAuOV0gPC0ga2VlcF9zY29yZVtpbnB1dF9zZXFzJFJOQT49MC45XSArIDEKICAgIGtlZXBfc2NvcmVbaW5wdXRfc2VxcyRsYXZpZGF2aXJpZGFlPj0wLjldIDwtIGtlZXBfc2NvcmVbaW5wdXRfc2VxcyRsYXZpZGF2aXJpZGFlPj0wLjldICsgMQogICAga2VlcF9zY29yZVtpbnB1dF9zZXFzJE5DTERWPj0wLjldIDwtIGtlZXBfc2NvcmVbaW5wdXRfc2VxcyROQ0xEVj49MC45XSArIDEKICB9CiAgCiAgaWYgKGluY2x1ZGVfdmlyc29ydGVyKSB7CiAgICBrZWVwX3Njb3JlW2lucHV0X3NlcXMkY2F0ZWdvcnk9PTFdIDwtIGtlZXBfc2NvcmVbaW5wdXRfc2VxcyRjYXRlZ29yeT09MV0gKyAxCiAgICBrZWVwX3Njb3JlW2lucHV0X3NlcXMkY2F0ZWdvcnk9PTJdIDwtIGtlZXBfc2NvcmVbaW5wdXRfc2VxcyRjYXRlZ29yeT09Ml0gKyAwLjUKICAgIGtlZXBfc2NvcmVbaW5wdXRfc2VxcyRjYXRlZ29yeT09NF0gPC0ga2VlcF9zY29yZVtpbnB1dF9zZXFzJGNhdGVnb3J5PT00XSArIDEKICAgIGtlZXBfc2NvcmVbaW5wdXRfc2VxcyRjYXRlZ29yeT09NV0gPC0ga2VlcF9zY29yZVtpbnB1dF9zZXFzJGNhdGVnb3J5PT01XSArIDAuNQogIH0KICAKICBpZiAoaW5jbHVkZV9kZWVwdmlyZmluZGVyKSB7CiAgICBrZWVwX3Njb3JlW2lucHV0X3NlcXMkc2NvcmU+PTAuNyAmIGlucHV0X3NlcXMkY2hlY2t2X2xlbmd0aDwyMDAwMF0gPC0ga2VlcF9zY29yZVtpbnB1dF9zZXFzJHNjb3JlPj0wLjcgJiBpbnB1dF9zZXFzJGNoZWNrdl9sZW5ndGg8MjAwMDBdICsgMC41CiAgICBrZWVwX3Njb3JlW2lucHV0X3NlcXMkc2NvcmU+PTAuOSAmIGlucHV0X3NlcXMkY2hlY2t2X2xlbmd0aDwyMDAwMF0gPC0ga2VlcF9zY29yZVtpbnB1dF9zZXFzJHNjb3JlPj0wLjkgJiBpbnB1dF9zZXFzJGNoZWNrdl9sZW5ndGg8MjAwMDBdICsgMC41CiAgfQogIAogIGlmIChpbmNsdWRlX3R1bmluZ192aXJhbCkgewogICAga2VlcF9zY29yZVtpbnB1dF9zZXFzJEthaWp1X1ZpcmFsPT0iVmlydXNlcyJdIDwtIGtlZXBfc2NvcmVbaW5wdXRfc2VxcyRLYWlqdV9WaXJhbD09IlZpcnVzZXMiXSArIDAuNQogICAga2VlcF9zY29yZVtpbnB1dF9zZXFzJGhhbGxtYXJrPjJdIDwtIGtlZXBfc2NvcmVbaW5wdXRfc2VxcyRoYWxsbWFyaz4yXSArIDEKICAgIGtlZXBfc2NvcmVbaW5wdXRfc2VxcyRwZXJjZW50X3Vua25vd24+PTc1ICYgaW5wdXRfc2VxcyRjaGVja3ZfbGVuZ3RoPDUwMDAwXSA8LSBrZWVwX3Njb3JlW2lucHV0X3NlcXMkcGVyY2VudF91bmtub3duPj03NSAmIGlucHV0X3NlcXMkY2hlY2t2X2xlbmd0aDw1MDAwMF0gKyAwLjUKICAgIGtlZXBfc2NvcmVbaW5wdXRfc2VxcyRwZXJjZW50X3ZpcmFsPj01MF0gPC0ga2VlcF9zY29yZVtpbnB1dF9zZXFzJHBlcmNlbnRfdmlyYWw+PTUwXSArIDAuNQogIH0KICAKICBpZiAoaW5jbHVkZV90dW5pbmdfbm90X3ZpcmFsKSB7CiAgICBrZWVwX3Njb3JlW2lucHV0X3NlcXMkS2FpanVfVmlyYWw9PSJjZWxsdWxhciBvcmdhbmlzbXMiXSA8LSBrZWVwX3Njb3JlW2lucHV0X3NlcXMkS2FpanVfVmlyYWw9PSJjZWxsdWxhciBvcmdhbmlzbXMiXSAtIDAuNQogICAga2VlcF9zY29yZVtpbnB1dF9zZXFzJGNoZWNrdl9ob3N0X2dlbmVzPjUwICYgIWlucHV0X3NlcXMkcHJvdmlydXNdIDwtIGtlZXBfc2NvcmVbaW5wdXRfc2VxcyRjaGVja3ZfaG9zdF9nZW5lcz41MCAmICFpbnB1dF9zZXFzJHByb3ZpcnVzXSAtIDEKICAgIGtlZXBfc2NvcmVbaW5wdXRfc2VxcyRjaGVja3ZfdmlyYWxfZ2VuZXM9PTAgJiBpbnB1dF9zZXFzJGNoZWNrdl9ob3N0X2dlbmVzPj0xXSA8LSBrZWVwX3Njb3JlW2lucHV0X3NlcXMkY2hlY2t2X3ZpcmFsX2dlbmVzPT0wICYgaW5wdXRfc2VxcyRjaGVja3ZfaG9zdF9nZW5lcz49MV0gLSAxCiAgICBrZWVwX3Njb3JlWygoaW5wdXRfc2VxcyRjaGVja3ZfdmlyYWxfZ2VuZXMqMykgPD0gaW5wdXRfc2VxcyRjaGVja3ZfaG9zdF9nZW5lcykgJiAhaW5wdXRfc2VxcyRwcm92aXJ1c10gPC0ga2VlcF9zY29yZVsoKGlucHV0X3NlcXMkY2hlY2t2X3ZpcmFsX2dlbmVzKjMpIDw9IGlucHV0X3NlcXMkY2hlY2t2X2hvc3RfZ2VuZXMpICYgIWlucHV0X3NlcXMkcHJvdmlydXNdIC0gMSAjIGNvbnNpZGVyIGFjY291bnRpbmcgZm9yIFJOQSB2aXJ1c2VzCiAgICBrZWVwX3Njb3JlW2lucHV0X3NlcXMkY2hlY2t2X2xlbmd0aD41MDAwMDAgJiBpbnB1dF9zZXFzJGhhbGxtYXJrPD0xXSA8LSBrZWVwX3Njb3JlW2lucHV0X3NlcXMkY2hlY2t2X2xlbmd0aD41MDAwMDAgJiBpbnB1dF9zZXFzJGhhbGxtYXJrPD0xXSAtIDEKICAgIGtlZXBfc2NvcmVbaW5wdXRfc2VxcyRjaGVja3ZfY29tcGxldGVuZXNzPD03NSAmIGlucHV0X3NlcXMkY2hlY2t2X2xlbmd0aDw9NTAwMF0gPC0ga2VlcF9zY29yZVtpbnB1dF9zZXFzJGNoZWNrdl9jb21wbGV0ZW5lc3M8PTc1ICYgaW5wdXRfc2VxcyRjaGVja3ZfbGVuZ3RoPD01MDAwXSAtIDAuNSAjIGhlbHBlZCB3aXRoIHByb3Rpc3QgY29udGFtaW5hdGlvbgogIH0KICAKICByZXR1cm4oa2VlcF9zY29yZSkKICAKfQpgYGAKCiMgQXNzZXNzaW5nIHBlcmZvcm1hbmNlIGFnYWluc3QgdGhlICJ0cnV0aCIKbm90ZSB0aGF0IHRoaXMgaXMgb25seSBhcyBhY2N1cmF0ZSBhcyB0aGUgYW5ub3RhdGlvbnMgb2YgdGhlIGlucHV0IHNlcXVlbmNlcwoKdGhpcyBmdW5jdGlvbiBjYWxjdWxhdGVzIHRoZSBwcmVjaXNpb24sIHJlY2FsbCwgYW5kIEYxIHNjb3JlIGZvciBlYWNoIHBpcGVsaW5lCmBgYHtyfQphc3Nlc3NfcGVyZm9ybWFuY2UgPC0gZnVuY3Rpb24oc2VxdHlwZSwga2VlcF9zY29yZSkgewogIAogIHRydWVwb3NpdGl2ZSA8LSByZXAoIm5vdCB2aXJhbCIsIGxlbmd0aChzZXF0eXBlKSkKICB0cnVlcG9zaXRpdmVbc2VxdHlwZT09InZpcnVzIl0gPC0gInZpcmFsIgogIAogICNtYWtlIGNvbmZ1c2lvbiBtYXRyaXgKICBjb25mdXNpb25fbWF0cml4IDwtIHJlcCgidHJ1ZSBuZWdhdGl2ZSIsIGxlbmd0aChrZWVwX3Njb3JlKSkKICBjb25mdXNpb25fbWF0cml4W3RydWVwb3NpdGl2ZT09InZpcmFsIiAmIGtlZXBfc2NvcmU8PTFdIDwtICJmYWxzZSBuZWdhdGl2ZSIKICBjb25mdXNpb25fbWF0cml4W3RydWVwb3NpdGl2ZT09InZpcmFsIiAmIGtlZXBfc2NvcmU+PTFdIDwtICJ0cnVlIHBvc2l0aXZlIgogIGNvbmZ1c2lvbl9tYXRyaXhbdHJ1ZXBvc2l0aXZlPT0ibm90IHZpcmFsIiAmIGtlZXBfc2NvcmU+PTFdIDwtICJmYWxzZSBwb3NpdGl2ZSIKICAKICBUUCA8LSB0YWJsZShjb25mdXNpb25fbWF0cml4KVs0XQogIEZQIDwtIHRhYmxlKGNvbmZ1c2lvbl9tYXRyaXgpWzJdCiAgVE4gPC0gdGFibGUoY29uZnVzaW9uX21hdHJpeClbM10KICBGTiA8LSB0YWJsZShjb25mdXNpb25fbWF0cml4KVsxXQogIAogIHByZWNpc2lvbiA8LSBUUC8oVFArRlApCiAgcmVjYWxsIDwtIFRQLyhUUCtGTikKICBGMSA8LSAyKnByZWNpc2lvbipyZWNhbGwvKHByZWNpc2lvbityZWNhbGwpCiAgCiAgTUNDIDwtIChUUCpUTi1GUCpGTikvc3FydChhcy5udW1lcmljKFRQK0ZQKSphcy5udW1lcmljKFRQK0ZOKSphcy5udW1lcmljKFROK0ZQKSphcy5udW1lcmljKFROK0ZOKSkKICAKICBhdWMgPC0gcm91bmQoYXVjKHRydWVwb3NpdGl2ZSwga2VlcF9zY29yZSksNCkKICAKICAjYnkgdHlwZSBtZXRyaWNzCiAgZnVuZ2FsX0ZQIDwtIHRhYmxlKGNvbmZ1c2lvbl9tYXRyaXhbc2VxdHlwZT09ImZ1bmdpIl0pWzJdCiAgcHJvdGlzdF9GUCA8LSB0YWJsZShjb25mdXNpb25fbWF0cml4W3NlcXR5cGU9PSJwcm90aXN0Il0pWzJdCiAgYmFjdGVyaWFsX0ZQIDwtIHRhYmxlKGNvbmZ1c2lvbl9tYXRyaXhbc2VxdHlwZT09ImJhY3RlcmlhIl0pWzJdCiAgdmlyYWxfRk4gPC0gdGFibGUoY29uZnVzaW9uX21hdHJpeFtzZXF0eXBlPT0idmlydXMiXSlbMV0KICAKICBwZXJmb3JtYW5jZSA8LSBjKHByZWNpc2lvbiwgcmVjYWxsLCBGMSwgTUNDLCBhdWMsIGZ1bmdhbF9GUCwgCiAgICAgICAgICAgICAgICAgICBwcm90aXN0X0ZQLCBiYWN0ZXJpYWxfRlAsIHZpcmFsX0ZOKQogIG5hbWVzKHBlcmZvcm1hbmNlKSA8LSBjKCJwcmVjaXNpb24iLCAicmVjYWxsIiwgIkYxIiwgIk1DQyIsICJBVUMiLCAiZnVuZ2FsX0ZQIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAicHJvdGlzdF9GUCIsICJiYWN0ZXJpYWxfRlAiLCAidmlyYWxfRk4iKQogIAogIHJldHVybihwZXJmb3JtYW5jZSkKfQpgYGAKCmNvbWJpbmF0aW9uIG9mIHRvb2xzIGxpc3QKYGBge3J9CmNvbWJvc19saXN0IDwtIGRhdGEuZnJhbWUodG9vbGNvbWJvPXJlcCgwLCA2NCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgdHVuZV9ub3RfdmlyYWw9cmVwKDAsIDY0KSwKICAgICAgICAgICAgICAgICAgICAgICAgICBEVkY9cmVwKDAsIDY0KSwKICAgICAgICAgICAgICAgICAgICAgICAgICB0dW5lX3ZpcmFsPXJlcCgwLCA2NCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgVklCUkFOVD1yZXAoMCwgNjQpLAogICAgICAgICAgICAgICAgICAgICAgICAgIFZTPXJlcCgwLCA2NCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgVlMyPXJlcCgwLCA2NCkpCnAgPC0gMQoKZm9yIChpIGluIGMoMCwxKSl7CiAgZm9yIChqIGluIGMoMCwxKSl7CiAgICBmb3IgKGsgaW4gYygwLDEpKXsKICAgICAgZm9yIChsIGluIGMoMCwxKSl7CiAgICAgICAgZm9yIChtIGluIGMoMCwxKSl7CiAgICAgICAgICBmb3IgKG4gaW4gYygwLDEpKXsKICAgICAgICAgICAgY29tYm9zX2xpc3QkdG9vbGNvbWJvW3BdIDwtIHBhc3RlKGksaixrLGwsbSxuKQogICAgICAgICAgICBjb21ib3NfbGlzdCR0b29sY29tYm8yW3BdIDwtIHBhc3RlKGlmKGkpeyJ0diJ9ZWxzZXsiMCJ9LGlmKGopeyJEVkYifWVsc2V7IjAifSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZihrKXsidG52In1lbHNleyIwIn0saWYobCl7IlZCIn1lbHNleyIwIn0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYobSl7IlZTIn1lbHNleyIwIn0saWYobil7IlZTMiJ9ZWxzZXsiMCJ9KQogICAgICAgICAgICBjb21ib3NfbGlzdCR0dW5lX25vdF92aXJhbFtwXSA8LSBpCiAgICAgICAgICAgIGNvbWJvc19saXN0JERWRltwXSA8LSBqCiAgICAgICAgICAgIGNvbWJvc19saXN0JHR1bmVfdmlyYWxbcF0gPC0gawogICAgICAgICAgICBjb21ib3NfbGlzdCRWSUJSQU5UW3BdIDwtIGwKICAgICAgICAgICAgY29tYm9zX2xpc3QkVlNbcF0gPC0gbQogICAgICAgICAgICBjb21ib3NfbGlzdCRWUzJbcF0gPC0gbgogICAgICAgICAgICBwIDwtIHArMQogICAgICAgICAgfQogICAgICAgIH0KICAgICAgfQogICAgfQogIH0KfQoKY29tYm9zX2xpc3QgPC0gY29tYm9zX2xpc3RbLTEsXQpgYGAKCnRoaXMgZnVuY3Rpb24gYnVpbGRzIGEgbGlzdCBvZiBhbGwgb2YgdGhlIGNvbWJpbmF0aW9ucyB0aGF0IHRoZSB1c2VyIHdhbnRzIHRvIAp0ZXN0LiAKSW4gdGhpcyBjYXNlLCB3ZSdyZSBjb21wYXJpbmcgdGhlIHBlcmZvcm1hbmNlIG9mIGFsbCB1bmlxdWUgY29tYmluYXRpb25zIG9mIHRoZSAKc2l4IHRvb2xzLgpgYGB7cn0KYnVpbGRfc2NvcmVfbGlzdCA8LSBmdW5jdGlvbihpbnB1dF9zZXFzLCBjb21ib3MpIHsKICBvdXRwdXQgPC0gZGF0YS5mcmFtZShwcmVjaXNpb249cmVwKDAsIG5yb3coY29tYm9zKSksCiAgICAgICAgICAgICAgICAgICAgICAgcmVjYWxsPXJlcCgwLCBucm93KGNvbWJvcykpLAogICAgICAgICAgICAgICAgICAgICAgIEYxPXJlcCgwLCBucm93KGNvbWJvcykpLAogICAgICAgICAgICAgICAgICAgICAgIE1DQz1yZXAoMCwgbnJvdyhjb21ib3MpKSwKICAgICAgICAgICAgICAgICAgICAgICBBVUM9cmVwKDAsIG5yb3coY29tYm9zKSksCiAgICAgICAgICAgICAgICAgICAgICAgZnVuZ2FsX0ZQPXJlcCgwLCBucm93KGNvbWJvcykpLAogICAgICAgICAgICAgICAgICAgICAgIHByb3Rpc3RfRlA9cmVwKDAsIG5yb3coY29tYm9zKSksCiAgICAgICAgICAgICAgICAgICAgICAgYmFjdGVyaWFsX0ZQPXJlcCgwLCBucm93KGNvbWJvcykpLAogICAgICAgICAgICAgICAgICAgICAgIHZpcmFsX0ZOPXJlcCgwLCBucm93KGNvbWJvcykpKQogIGZvciAoaSBpbiAxOm5yb3coY29tYm9zKSkgewogICAga2VlcF9zY29yZSA8LSBnZXR0aW5nX3ZpcmFsX3NldF8xKGlucHV0X3NlcXMsIGluY2x1ZGVfdmlicmFudCA9IGNvbWJvcyRWSUJSQU5UW2ldLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGluY2x1ZGVfdmlyc29ydGVyID0gY29tYm9zJFZTW2ldLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGluY2x1ZGVfdmlyc29ydGVyMiA9IGNvbWJvcyRWUzJbaV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaW5jbHVkZV90dW5pbmdfdmlyYWwgPSBjb21ib3MkdHVuZV92aXJhbFtpXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpbmNsdWRlX3R1bmluZ19ub3RfdmlyYWwgPSBjb21ib3MkdHVuZV9ub3RfdmlyYWxbaV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaW5jbHVkZV9kZWVwdmlyZmluZGVyID0gY29tYm9zJERWRltpXSkKICAKICAgIG91dHB1dFtpLDE6OV0gPC0gYXNzZXNzX3BlcmZvcm1hbmNlKGlucHV0X3NlcXMkc2VxdHlwZSwga2VlcF9zY29yZSkKICAgIAogICAgb3V0cHV0JHRvb2xjb21ib1tpXSA8LSBwYXN0ZShjb21ib3MkdHVuZV92aXJhbFtpXSxjb21ib3MkRFZGW2ldLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb21ib3MkdHVuZV9ub3RfdmlyYWxbaV0sIGNvbWJvcyRWSUJSQU5UW2ldLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb21ib3MkVlNbaV0sIGNvbWJvcyRWUzJbaV0pCiAgfQogIAogIG91dHB1dFtpcy5uYShvdXRwdXQpXSA8LSAwCgogIHJldHVybiAob3V0cHV0KQp9CmBgYAoKIyMgQ2FsY3VsYXRlIHRoZSBwZXJmb3JtYW5jZSBvZiBlYWNoIHBpcGVsaW5lCmBgYHtyfQphY2N1cmFjeV9zY29yZXMgPC0gZGF0YS5mcmFtZSh0ZXN0aW5nX3NldF9pbmRleD1yZXAoMCwgbnJvdyhjb21ib3NfbGlzdCkqMTApLAogICAgICAgICAgICAgICAgICAgICAgcHJlY2lzaW9uPXJlcCgwLCBucm93KGNvbWJvc19saXN0KSoxMCksCiAgICAgICAgICAgICAgICAgICAgICAgcmVjYWxsPXJlcCgwLCBucm93KGNvbWJvc19saXN0KSoxMCksCiAgICAgICAgICAgICAgICAgICAgICAgRjE9cmVwKDAsIG5yb3coY29tYm9zX2xpc3QpKjEwKSwKICAgICAgICAgICAgICAgICAgICAgICBNQ0M9cmVwKDAsIG5yb3coY29tYm9zX2xpc3QpKjEwKSwgCiAgICAgICAgICAgICAgICAgICAgICBBVUM9cmVwKDAsIG5yb3coY29tYm9zX2xpc3QpKjEwKSwKICAgICAgICAgICAgICAgICAgICAgIGZ1bmdhbF9GUD1yZXAoMCwgbnJvdyhjb21ib3NfbGlzdCkqMTApLAogICAgICAgICAgICAgICAgICAgICAgcHJvdGlzdF9GUD1yZXAoMCwgbnJvdyhjb21ib3NfbGlzdCkqMTApLAogICAgICAgICAgICAgICAgICAgICAgYmFjdGVyaWFsX0ZQPXJlcCgwLCBucm93KGNvbWJvc19saXN0KSoxMCksCiAgICAgICAgICAgICAgICAgICAgICB2aXJhbF9GTj1yZXAoMCwgbnJvdyhjb21ib3NfbGlzdCkqMTApKQoKYWNjdXJhY3lfc2NvcmVzIDwtIGNiaW5kKHRlc3Rpbmdfc2V0X2luZGV4PXJlcCgxLCBucm93KGNvbWJvc19saXN0KSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJ1aWxkX3Njb3JlX2xpc3QodmlydXNlc1t2aXJ1c2VzJEluZGV4PT0xLF0sIGNvbWJvc19saXN0KSkKZm9yIChpIGluIDI6MTApIHsKICBhY2N1cmFjeV9zY29yZXMgPC0gcmJpbmQoYWNjdXJhY3lfc2NvcmVzLAogICAgICAgICAgICAgICAgICAgICAgICAgICBjYmluZCh0ZXN0aW5nX3NldF9pbmRleD1yZXAoaSwgbnJvdyhjb21ib3NfbGlzdCkpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBidWlsZF9zY29yZV9saXN0KHZpcnVzZXNbdmlydXNlcyRJbmRleD09aSxdLCBjb21ib3NfbGlzdCkpKQp9CmBgYAoKYGBge3J9CmxpYnJhcnkoInN0cmluZ3IiKQpgYGAKCmBgYHtyfQphY2N1cmFjeV9zY29yZXMkbnVtdG9vbHMgPC0gc3RyX2NvdW50KGFjY3VyYWN5X3Njb3JlcyR0b29sY29tYm8sICIxIikKI2FjY3VyYWN5X3Njb3JlcyA8LSBhY2N1cmFjeV9zY29yZXNbb3JkZXIoYWNjdXJhY3lfc2NvcmVzJG51bXRvb2xzLCBkZWNyZWFzaW5nPUYpLF0KYWNjdXJhY3lfc2NvcmVzIDwtIGFjY3VyYWN5X3Njb3Jlc1tvcmRlcihhY2N1cmFjeV9zY29yZXMkTUNDLCBkZWNyZWFzaW5nPUYpLF0KYWNjdXJhY3lfc2NvcmVzJHRvb2xjb21ibyA8LSBmYWN0b3IoYWNjdXJhY3lfc2NvcmVzJHRvb2xjb21ibywgbGV2ZWxzID0gdW5pcXVlKGFjY3VyYWN5X3Njb3JlcyR0b29sY29tYm8pKQphY2N1cmFjeV9zY29yZXMkbnVtdG9vbHMgPC0gYXMuZmFjdG9yKGFjY3VyYWN5X3Njb3JlcyRudW10b29scykKYGBgCgoKIyMgVmlzdWFsaXplIGhvdyB0aGUgcHJlY2lzaW9uLCByZWNhbGwsIGFuZCBGMSBzY29yZXMgY2hhbmdlIGFjcm9zcyBwaXBlbGluZXMuCmBgYHtyfQpwYWwgPC0gZ2d0aGVtZXM6OnRhYmxlYXVfY29sb3JfcGFsKHBhbGV0dGU9IlRhYmxlYXUgMTAiLCB0eXBlPSJyZWd1bGFyIikKcDIgPC0gZ2dwbG90KGFjY3VyYWN5X3Njb3JlcywgYWVzKHg9dG9vbGNvbWJvLCB5PUYxLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yPW51bXRvb2xzLCBmaWxsPW51bXRvb2xzKSkgKwogIGdlb21fcG9pbnQoYWxwaGE9MC41KSArCiAgdGhlbWVfbGlnaHQoKSArCiAgdGhlbWUoCiAgICBwYW5lbC5ncmlkLm1ham9yLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X2JsYW5rKCksCiAgICBheGlzLnRpY2tzLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICBsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwKICAgIGF4aXMudGV4dC55PWVsZW1lbnRfdGV4dChzaXplPTE0KSwKICAgIGF4aXMudGV4dC54PWVsZW1lbnRfdGV4dChzaXplPTE0LCBhbmdsZSA9IDkwKSwKICAgIGxlZ2VuZC50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTEyKSwKICAgIGF4aXMudGl0bGU9ZWxlbWVudF90ZXh0KHNpemU9MTYpLAogICkgKwogIHhsYWIoIlRvb2wgQ29tYmluYXRpb24gKHR2LCBEVkYsIHRudiwgVkIsIFZTLCBWUzIpIikgKwogIHlsYWIoIkYxIFNjb3JlIikKcDIKZ2dwbG90KGFjY3VyYWN5X3Njb3JlcywgYWVzKHg9dG9vbGNvbWJvLCB5PXByZWNpc2lvbiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvcj1udW10b29scywgZmlsbD1udW10b29scykpICsKICBnZW9tX3BvaW50KGFscGhhPTAuNSkgKwogIHRoZW1lX2xpZ2h0KCkgKwogIHRoZW1lKAogICAgcGFuZWwuZ3JpZC5tYWpvci55ID0gZWxlbWVudF9ibGFuaygpLAogICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9ibGFuaygpLAogICAgYXhpcy50aWNrcy55ID0gZWxlbWVudF9ibGFuaygpLAogICAgbGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsCiAgICBheGlzLnRleHQueT1lbGVtZW50X3RleHQoc2l6ZT0xNCksCiAgICBheGlzLnRleHQueD1lbGVtZW50X3RleHQoc2l6ZT0xNCwgYW5nbGUgPSA5MCksCiAgICBsZWdlbmQudGV4dD1lbGVtZW50X3RleHQoc2l6ZT0xMiksCiAgICBheGlzLnRpdGxlPWVsZW1lbnRfdGV4dChzaXplPTE2KSwKICApICsKICB4bGFiKCJUb29sIENvbWJpbmF0aW9uICh0diwgRFZGLCB0bnYsIFZCLCBWUywgVlMyKSIpICsKICB5bGFiKCJQcmVjaXNpb24iKQpnZ3Bsb3QoYWNjdXJhY3lfc2NvcmVzLCBhZXMoeD10b29sY29tYm8sIHk9cmVjYWxsLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yPW51bXRvb2xzLCBmaWxsPW51bXRvb2xzKSkgKwogIGdlb21fcG9pbnQoYWxwaGE9MC41KSArCiAgdGhlbWVfbGlnaHQoKSArCiAgdGhlbWUoCiAgICBwYW5lbC5ncmlkLm1ham9yLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X2JsYW5rKCksCiAgICBheGlzLnRpY2tzLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICBsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwKICAgIGF4aXMudGV4dC55PWVsZW1lbnRfdGV4dChzaXplPTE0KSwKICAgIGF4aXMudGV4dC54PWVsZW1lbnRfdGV4dChzaXplPTE0LCBhbmdsZSA9IDkwKSwKICAgIGxlZ2VuZC50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTEyKSwKICAgIGF4aXMudGl0bGU9ZWxlbWVudF90ZXh0KHNpemU9MTYpLAogICkgKwogIHhsYWIoIlRvb2wgQ29tYmluYXRpb24gKHR2LCBEVkYsIHRudiwgVkIsIFZTLCBWUzIpIikgKwogIHlsYWIoIlJlY2FsbCIpCmdncGxvdChhY2N1cmFjeV9zY29yZXMsIGFlcyh4PXByZWNpc2lvbiwgeT1yZWNhbGwsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3I9bnVtdG9vbHMsIGZpbGw9bnVtdG9vbHMpKSArCiAgZ2VvbV9wb2ludChhbHBoYT0wLjUpICsKICB0aGVtZV9saWdodCgpICsKICB0aGVtZSgKICAgIHBhbmVsLmdyaWQubWFqb3IueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIGF4aXMudGlja3MueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iLAogICAgYXhpcy50ZXh0Lnk9ZWxlbWVudF90ZXh0KHNpemU9MTQpLAogICAgYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KHNpemU9MTQsIGFuZ2xlID0gOTApLAogICAgbGVnZW5kLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9MTIpLAogICAgYXhpcy50aXRsZT1lbGVtZW50X3RleHQoc2l6ZT0xNiksCiAgKSArCiAgeGxhYigiUHJlY2lzaW9uIikgKwogIHlsYWIoIlJlY2FsbCIpCmdncGxvdChhY2N1cmFjeV9zY29yZXMsIGFlcyh4PXRvb2xjb21ibywgeT1hYnMocHJlY2lzaW9uLXJlY2FsbCksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3I9bnVtdG9vbHMsIGZpbGw9bnVtdG9vbHMpKSArCiAgZ2VvbV9wb2ludChhbHBoYT0wLjUpICsKICB0aGVtZV9saWdodCgpICsKICB0aGVtZSgKICAgIHBhbmVsLmdyaWQubWFqb3IueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIGF4aXMudGlja3MueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iLAogICAgYXhpcy50ZXh0Lnk9ZWxlbWVudF90ZXh0KHNpemU9MTQpLAogICAgYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KHNpemU9MTQsIGFuZ2xlID0gOTApLAogICAgbGVnZW5kLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9MTIpLAogICAgYXhpcy50aXRsZT1lbGVtZW50X3RleHQoc2l6ZT0xNiksCiAgKSArCiAgeGxhYigiVG9vbCBDb21iaW5hdGlvbiAodHYsIERWRiwgdG52LCBWQiwgVlMsIFZTMikiKSArCiAgeWxhYigiUHJlY2lzaW9uLVJlY2FsbCIpCmdncGxvdChhY2N1cmFjeV9zY29yZXMsIGFlcyh4PXRvb2xjb21ibywgeT1NQ0MsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3I9bnVtdG9vbHMsIGZpbGw9bnVtdG9vbHMpKSArCiAgZ2VvbV9wb2ludChhbHBoYT0wLjUpICsKICB0aGVtZV9saWdodCgpICsKICB0aGVtZSgKICAgIHBhbmVsLmdyaWQubWFqb3IueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIGF4aXMudGlja3MueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iLAogICAgYXhpcy50ZXh0Lnk9ZWxlbWVudF90ZXh0KHNpemU9MTQpLAogICAgYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KHNpemU9MTQsIGFuZ2xlID0gOTApLAogICAgbGVnZW5kLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9MTIpLAogICAgYXhpcy50aXRsZT1lbGVtZW50X3RleHQoc2l6ZT0xNiksCiAgKSArCiAgeGxhYigiVG9vbCBDb21iaW5hdGlvbiAodHYsIERWRiwgdG52LCBWQiwgVlMsIFZTMikiKSArCiAgeWxhYigiTUNDIikKZ2dwbG90KGFjY3VyYWN5X3Njb3JlcywgYWVzKHg9dG9vbGNvbWJvLCB5PUFVQywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvcj1udW10b29scywgZmlsbD1udW10b29scykpICsKICBnZW9tX3BvaW50KGFscGhhPTAuNSkgKwogIHRoZW1lX2xpZ2h0KCkgKwogIHRoZW1lKAogICAgcGFuZWwuZ3JpZC5tYWpvci55ID0gZWxlbWVudF9ibGFuaygpLAogICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9ibGFuaygpLAogICAgYXhpcy50aWNrcy55ID0gZWxlbWVudF9ibGFuaygpLAogICAgbGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsCiAgICBheGlzLnRleHQueT1lbGVtZW50X3RleHQoc2l6ZT0xNCksCiAgICBheGlzLnRleHQueD1lbGVtZW50X3RleHQoc2l6ZT0xNCwgYW5nbGUgPSA5MCksCiAgICBsZWdlbmQudGV4dD1lbGVtZW50X3RleHQoc2l6ZT0xMiksCiAgICBheGlzLnRpdGxlPWVsZW1lbnRfdGV4dChzaXplPTE2KSwKICApICsKICB4bGFiKCJUb29sIENvbWJpbmF0aW9uICh0diwgRFZGLCB0bnYsIFZCLCBWUywgVlMyKSIpICsKICB5bGFiKCJBVUMiKQpnZ3Bsb3QoYWNjdXJhY3lfc2NvcmVzLCBhZXMoeD10b29sY29tYm8sIHk9ZnVuZ2FsX0ZQLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yPW51bXRvb2xzLCBmaWxsPW51bXRvb2xzKSkgKwogIGdlb21fcG9pbnQoYWxwaGE9MC41KSArCiAgdGhlbWVfbGlnaHQoKSArCiAgdGhlbWUoCiAgICBwYW5lbC5ncmlkLm1ham9yLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X2JsYW5rKCksCiAgICBheGlzLnRpY2tzLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICBsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwKICAgIGF4aXMudGV4dC55PWVsZW1lbnRfdGV4dChzaXplPTE0KSwKICAgIGF4aXMudGV4dC54PWVsZW1lbnRfdGV4dChzaXplPTE0LCBhbmdsZSA9IDkwKSwKICAgIGxlZ2VuZC50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTEyKSwKICAgIGF4aXMudGl0bGU9ZWxlbWVudF90ZXh0KHNpemU9MTYpLAogICkgKwogIHhsYWIoIlRvb2wgQ29tYmluYXRpb24gKHR2LCBEVkYsIHRudiwgVkIsIFZTLCBWUzIpIikgKwogIHlsYWIoIkZ1bmdhbCBGYWxzZSBQb3NpdGl2ZXMiKQoKZ2dwbG90KGFjY3VyYWN5X3Njb3JlcywgYWVzKHg9dG9vbGNvbWJvLCB5PXByb3Rpc3RfRlAsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3I9bnVtdG9vbHMsIGZpbGw9bnVtdG9vbHMpKSArCiAgZ2VvbV9wb2ludChhbHBoYT0wLjUpICsKICB0aGVtZV9saWdodCgpICsKICB0aGVtZSgKICAgIHBhbmVsLmdyaWQubWFqb3IueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIGF4aXMudGlja3MueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iLAogICAgYXhpcy50ZXh0Lnk9ZWxlbWVudF90ZXh0KHNpemU9MTQpLAogICAgYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KHNpemU9MTQsIGFuZ2xlID0gOTApLAogICAgbGVnZW5kLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9MTIpLAogICAgYXhpcy50aXRsZT1lbGVtZW50X3RleHQoc2l6ZT0xNiksCiAgKSArCiAgeGxhYigiVG9vbCBDb21iaW5hdGlvbiAodHYsIERWRiwgdG52LCBWQiwgVlMsIFZTMikiKSArCiAgeWxhYigiUHJvdGlzdCBGYWxzZSBQb3NpdGl2ZXMiKQoKZ2dwbG90KGFjY3VyYWN5X3Njb3JlcywgYWVzKHg9dG9vbGNvbWJvLCB5PWJhY3RlcmlhbF9GUCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvcj1udW10b29scywgZmlsbD1udW10b29scykpICsKICBnZW9tX3BvaW50KGFscGhhPTAuNSkgKwogIHRoZW1lX2xpZ2h0KCkgKwogIHRoZW1lKAogICAgcGFuZWwuZ3JpZC5tYWpvci55ID0gZWxlbWVudF9ibGFuaygpLAogICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9ibGFuaygpLAogICAgYXhpcy50aWNrcy55ID0gZWxlbWVudF9ibGFuaygpLAogICAgbGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsCiAgICBheGlzLnRleHQueT1lbGVtZW50X3RleHQoc2l6ZT0xNCksCiAgICBheGlzLnRleHQueD1lbGVtZW50X3RleHQoc2l6ZT0xNCwgYW5nbGUgPSA5MCksCiAgICBsZWdlbmQudGV4dD1lbGVtZW50X3RleHQoc2l6ZT0xMiksCiAgICBheGlzLnRpdGxlPWVsZW1lbnRfdGV4dChzaXplPTE2KSwKICApICsKICB4bGFiKCJUb29sIENvbWJpbmF0aW9uICh0diwgRFZGLCB0bnYsIFZCLCBWUywgVlMyKSIpICsKICB5bGFiKCJCYWN0ZXJpYWwgRmFsc2UgUG9zaXRpdmVzIikKZ2dwbG90KGFjY3VyYWN5X3Njb3JlcywgYWVzKHg9dG9vbGNvbWJvLCB5PXZpcmFsX0ZOLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yPW51bXRvb2xzLCBmaWxsPW51bXRvb2xzKSkgKwogIGdlb21fcG9pbnQoYWxwaGE9MC41KSArCiAgdGhlbWVfbGlnaHQoKSArCiAgdGhlbWUoCiAgICBwYW5lbC5ncmlkLm1ham9yLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X2JsYW5rKCksCiAgICBheGlzLnRpY2tzLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICBsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwKICAgIGF4aXMudGV4dC55PWVsZW1lbnRfdGV4dChzaXplPTE0KSwKICAgIGF4aXMudGV4dC54PWVsZW1lbnRfdGV4dChzaXplPTE0LCBhbmdsZSA9IDkwKSwKICAgIGxlZ2VuZC50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTEyKSwKICAgIGF4aXMudGl0bGU9ZWxlbWVudF90ZXh0KHNpemU9MTYpLAogICkgKwogIHhsYWIoIlRvb2wgQ29tYmluYXRpb24gKHR2LCBEVkYsIHRudiwgVkIsIFZTLCBWUzIpIikgKwogIHlsYWIoIlZpcmFsIEZhbHNlIE5lZ2F0aXZlcyIpCmBgYAoKYGBge3J9CndyaXRlX3RzdihhY2N1cmFjeV9zY29yZXMsICIyMDIyMTAyOV9hY2N1cmFjeV9zY29yZXMudHN2IikKYGBgCgp0byBkbzogYWRkIGluIGNsdXN0ZXJpbmcgYW5kIG9yZGluYXRpb24gbGlrZSBpbiB0aGUgZHJpbmtpbmcgd2F0ZXIgUiBub3RlYm9vawoKIyBFeHBlcmltZW50aW5nCgojIyBoaWdoIHByZWNpc2lvbiBleGFtcGxlCmBgYHtyfQp2aXJ1c2VzJGtlZXBfc2NvcmVfaGlnaF9wcmVjaXNpb24gPC0gZ2V0dGluZ192aXJhbF9zZXRfMSh2aXJ1c2VzLCBpbmNsdWRlX2RlZXB2aXJmaW5kZXIgPSBULAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaW5jbHVkZV92aWJyYW50ID0gVCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGluY2x1ZGVfdmlyc29ydGVyMiA9IEYsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpbmNsdWRlX3R1bmluZ192aXJhbCA9IEYsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpbmNsdWRlX3R1bmluZ19ub3RfdmlyYWwgPSBULAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaW5jbHVkZV92aXJzb3J0ZXIgPSBGKQpgYGAKCgpgYGB7cn0KdmlydXNlcyRjb25mdXNpb25fbWF0cml4X2hpZ2hfcHJlY2lzaW9uIDwtICJ0cnVlIG5lZ2F0aXZlIgp2aXJ1c2VzJGNvbmZ1c2lvbl9tYXRyaXhfaGlnaF9wcmVjaXNpb25bdmlydXNlcyRzZXF0eXBlPT0idmlydXMiICYgdmlydXNlcyRrZWVwX3Njb3JlX2hpZ2hfcHJlY2lzaW9uPDFdIDwtICJmYWxzZSBuZWdhdGl2ZSIKdmlydXNlcyRjb25mdXNpb25fbWF0cml4X2hpZ2hfcHJlY2lzaW9uW3ZpcnVzZXMkc2VxdHlwZT09InZpcnVzIiAmIHZpcnVzZXMka2VlcF9zY29yZV9oaWdoX3ByZWNpc2lvbj49MV0gPC0gInRydWUgcG9zaXRpdmUiCnZpcnVzZXMkY29uZnVzaW9uX21hdHJpeF9oaWdoX3ByZWNpc2lvblt2aXJ1c2VzJHNlcXR5cGUhPSJ2aXJ1cyIgJiB2aXJ1c2VzJGtlZXBfc2NvcmVfaGlnaF9wcmVjaXNpb24+PTFdIDwtICJmYWxzZSBwb3NpdGl2ZSIKYGBgCgp2aXN1YWxpemluZyBjb25mdXNpb24gbWF0cml4IGJ5IHRheGEKYGBge3J9CmNvbmZ1c2lvbl9ieV90YXhhIDwtIG1lbHQodGFibGUodmlydXNlcyRjb25mdXNpb25fbWF0cml4X2hpZ2hfcHJlY2lzaW9uLCB2aXJ1c2VzJHNlcXR5cGUsIHZpcnVzZXMkSW5kZXgpKQpjb2xuYW1lcyhjb25mdXNpb25fYnlfdGF4YSkgPC0gYygiY29uZnVzaW9uX21hdHJpeCIsICJzZXF0eXBlIiwiSW5kZXgiLCAiY291bnQiKQpgYGAKCmBgYHtyfQpsZW5ndGgoZ3JlcCgidHJ1ZSIsIHZpcnVzZXMkY29uZnVzaW9uX21hdHJpeF9oaWdoX3ByZWNpc2lvbikpL25yb3codmlydXNlcykKYGBgCgpgYGB7cn0KcGFsIDwtIGdndGhlbWVzOjp0YWJsZWF1X2NvbG9yX3BhbChwYWxldHRlPSJUYWJsZWF1IDEwIiwgdHlwZT0icmVndWxhciIpCmBgYAoKYGBge3J9CmdncGxvdChjb25mdXNpb25fYnlfdGF4YSwgYWVzKHg9Y291bnQsIHk9YXMuZmFjdG9yKEluZGV4KSwKICAgICAgICAgICAgICAgICAgIGZpbGw9Y29uZnVzaW9uX21hdHJpeCwKICAgICAgICAgICAgICAgICAgIGNvbG9yPWNvbmZ1c2lvbl9tYXRyaXgpKSArCiAgZ2VvbV9iYXIoc3RhdD0iaWRlbnRpdHkiKSArCiAgdGhlbWVfbGlnaHQoKSArCiAgdGhlbWUoCiAgICBwYW5lbC5ncmlkLm1ham9yLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X2JsYW5rKCksCiAgICBheGlzLnRpY2tzLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICBsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwKICAgIGF4aXMudGV4dC55PWVsZW1lbnRfdGV4dChzaXplPTE0KSwKICAgIGF4aXMudGV4dC54PWVsZW1lbnRfdGV4dChzaXplPTE0KSwKICAgIGxlZ2VuZC50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTEyKSwKICAgIGF4aXMudGl0bGU9ZWxlbWVudF90ZXh0KHNpemU9MTYpLAogICkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKG5hbWU9IiIsCiAgICAgICAgICAgICAgICAgICAgIHZhbHVlcyA9IGFscGhhKHJldihwYWwoNCkpLCAwLjUpLAogICAgICAgICAgICAgICAgICAgIGxhYmVscz1jKCJmYWxzZSBuZWdhdGl2ZSIsICJmYWxzZSBwb3NpdGl2ZSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0cnVlIG5lZ2F0aXZlIiwgInRydWUgcG9zaXRpdmUiKSkgKwogIHNjYWxlX2NvbG9yX21hbnVhbChuYW1lPSIiLAogICAgICAgICAgICAgICAgICAgICB2YWx1ZXMgPSBhbHBoYShyZXYocGFsKDQpKSwgMSksCiAgICAgICAgICAgICAgICAgICAgbGFiZWxzPWMoImZhbHNlIG5lZ2F0aXZlIiwgImZhbHNlIHBvc2l0aXZlIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInRydWUgbmVnYXRpdmUiLCAidHJ1ZSBwb3NpdGl2ZSIpKSArCiAgeGxhYigiTnVtYmVyIG9mIFNlcXVlbmNlcyIpICsKICB5bGFiKCIiKSArIAogIGZhY2V0X3dyYXAofnNlcXR5cGUsIHNjYWxlcyA9ICJmcmVlIikgKwogIGNvb3JkX2ZsaXAoKQpgYGAKdGhpcyBydWxlIHNldCBoYWQgdGhlIGhpZ2hlc3QgcHJlY2lzaW9uLCBidXQgYXMgeW91IGNhbiBzZWUsIHRoaXMgY29tZXMgd2l0aCBhIGJpZyBzYWNyaWZpY2UgaW4gcmVjYWxsCgoKCiMjIGhpZ2ggTUNDIGV4YW1wbGUKYGBge3J9CnZpcnVzZXMka2VlcF9zY29yZV9oaWdoX01DQyA8LSBnZXR0aW5nX3ZpcmFsX3NldF8xKHZpcnVzZXMsIGluY2x1ZGVfZGVlcHZpcmZpbmRlciA9IEYsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpbmNsdWRlX3ZpYnJhbnQgPSBULAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaW5jbHVkZV92aXJzb3J0ZXIyID0gVCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGluY2x1ZGVfdHVuaW5nX3ZpcmFsID0gVCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGluY2x1ZGVfdHVuaW5nX25vdF92aXJhbCA9IFQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpbmNsdWRlX3ZpcnNvcnRlciA9IFQpCmBgYAoKCmBgYHtyfQp2aXJ1c2VzJGNvbmZ1c2lvbl9tYXRyaXhfaGlnaF9NQ0MgPC0gInRydWUgbmVnYXRpdmUiCnZpcnVzZXMkY29uZnVzaW9uX21hdHJpeF9oaWdoX01DQ1t2aXJ1c2VzJHNlcXR5cGU9PSJ2aXJ1cyIgJiB2aXJ1c2VzJGtlZXBfc2NvcmVfaGlnaF9NQ0M8MV0gPC0gImZhbHNlIG5lZ2F0aXZlIgp2aXJ1c2VzJGNvbmZ1c2lvbl9tYXRyaXhfaGlnaF9NQ0NbdmlydXNlcyRzZXF0eXBlPT0idmlydXMiICYgdmlydXNlcyRrZWVwX3Njb3JlX2hpZ2hfTUNDPj0xXSA8LSAidHJ1ZSBwb3NpdGl2ZSIKdmlydXNlcyRjb25mdXNpb25fbWF0cml4X2hpZ2hfTUNDW3ZpcnVzZXMkc2VxdHlwZSE9InZpcnVzIiAmIHZpcnVzZXMka2VlcF9zY29yZV9oaWdoX01DQz49MV0gPC0gImZhbHNlIHBvc2l0aXZlIgpgYGAKCmFjY3VyYWN5OgpgYGB7cn0KbGVuZ3RoKGdyZXAoInRydWUiLCB2aXJ1c2VzJGNvbmZ1c2lvbl9tYXRyaXhfaGlnaF9NQ0MpKS9ucm93KHZpcnVzZXMpCmBgYAoKcmVjYWxsCmBgYHtyfQpsZW5ndGgoZ3JlcCgidHJ1ZSBwb3NpdGl2ZSIsIHZpcnVzZXMkY29uZnVzaW9uX21hdHJpeF9oaWdoX01DQykpL2xlbmd0aChncmVwKCJ2aXJ1cyIsIHZpcnVzZXMkc2VxdHlwZSkpCmBgYAoKYGBge3J9CnZpcnVzZXMkc2l6ZV9jbGFzcyA8LSAiMy0xMGtiIgp2aXJ1c2VzJHNpemVfY2xhc3NbdmlydXNlcyRjaGVja3ZfbGVuZ3RoPjEwMDAwXSA8LSAiPjEwa2IiCmBgYAoKCgp2aXN1YWxpemluZyBjb25mdXNpb24gbWF0cml4IGJ5IHRheGEKYGBge3J9CmNvbmZ1c2lvbl9ieV90YXhhIDwtIHZpcnVzZXMgJT4lIGNvdW50KGNvbmZ1c2lvbl9tYXRyaXhfaGlnaF9NQ0MsIHNlcXR5cGUsIEluZGV4KQoKY29sbmFtZXMoY29uZnVzaW9uX2J5X3RheGEpIDwtIGMoImNvbmZ1c2lvbl9tYXRyaXgiLCAic2VxdHlwZSIsImluZGV4IiwgImNvdW50IikKYGBgCgoKYGBge3J9CnBhbCA8LSBnZ3RoZW1lczo6dGFibGVhdV9jb2xvcl9wYWwocGFsZXR0ZT0iVGFibGVhdSAxMCIsIHR5cGU9InJlZ3VsYXIiKQpgYGAKCmBgYHtyfQpnZ3Bsb3QoY29uZnVzaW9uX2J5X3RheGEsIGFlcyh4PWNvdW50LCB5PWFzLmZhY3RvcihpbmRleCksCiAgICAgICAgICAgICAgICAgICBmaWxsPWNvbmZ1c2lvbl9tYXRyaXgsCiAgICAgICAgICAgICAgICAgICBjb2xvcj1jb25mdXNpb25fbWF0cml4KSkgKwogIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5IikgKwogIHRoZW1lX2xpZ2h0KCkgKwogIHRoZW1lKAogICAgcGFuZWwuZ3JpZC5tYWpvci55ID0gZWxlbWVudF9ibGFuaygpLAogICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9ibGFuaygpLAogICAgYXhpcy50aWNrcy55ID0gZWxlbWVudF9ibGFuaygpLAogICAgbGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsCiAgICBheGlzLnRleHQueT1lbGVtZW50X3RleHQoc2l6ZT0xNCksCiAgICBheGlzLnRleHQueD1lbGVtZW50X3RleHQoc2l6ZT0xNCksCiAgICBsZWdlbmQudGV4dD1lbGVtZW50X3RleHQoc2l6ZT0xMiksCiAgICBheGlzLnRpdGxlPWVsZW1lbnRfdGV4dChzaXplPTE2KSwKICApICsKICBzY2FsZV9maWxsX21hbnVhbChuYW1lPSIiLAogICAgICAgICAgICAgICAgICAgICB2YWx1ZXMgPSBhbHBoYShyZXYocGFsKDQpKSwgMC41KSwKICAgICAgICAgICAgICAgICAgICBsYWJlbHM9YygiZmFsc2UgbmVnYXRpdmUiLCAiZmFsc2UgcG9zaXRpdmUiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHJ1ZSBuZWdhdGl2ZSIsICJ0cnVlIHBvc2l0aXZlIikpICsKICBzY2FsZV9jb2xvcl9tYW51YWwobmFtZT0iIiwKICAgICAgICAgICAgICAgICAgICAgdmFsdWVzID0gYWxwaGEocmV2KHBhbCg0KSksIDEpLAogICAgICAgICAgICAgICAgICAgIGxhYmVscz1jKCJmYWxzZSBuZWdhdGl2ZSIsICJmYWxzZSBwb3NpdGl2ZSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0cnVlIG5lZ2F0aXZlIiwgInRydWUgcG9zaXRpdmUiKSkgKwogIHhsYWIoIk51bWJlciBvZiBTZXF1ZW5jZXMiKSArCiAgeWxhYigiIikgKyAKICBmYWNldF93cmFwKH5zZXF0eXBlLCBzY2FsZXMgPSAiZnJlZSIpICsKICBjb29yZF9mbGlwKCkKYGBgCnBlcmNlbnQgdmlyYWwKCnZpc3VhbGl6aW5nIGNvbmZ1c2lvbiBtYXRyaXggYnkgdGF4YQpgYGB7cn0KY29uZnVzaW9uX2J5X3RheGEgPC0gdmlydXNlcyAlPiUgY291bnQoY29uZnVzaW9uX21hdHJpeF9oaWdoX01DQywgc2VxdHlwZSwgc2l6ZV9jbGFzcywgSW5kZXgpCgpjb2xuYW1lcyhjb25mdXNpb25fYnlfdGF4YSkgPC0gYygiY29uZnVzaW9uX21hdHJpeCIsICJzZXF0eXBlIiwic2l6ZSIsICJpbmRleCIsICJjb3VudCIpCmBgYAoKYGBge3J9CmNvbmZ1c2lvbl92aXJfY2FsbGVkIDwtIGNvbmZ1c2lvbl9ieV90YXhhICU+JSBmaWx0ZXIoY29uZnVzaW9uX21hdHJpeD09InRydWUgcG9zaXRpdmUiIHwgY29uZnVzaW9uX21hdHJpeD09ImZhbHNlIHBvc2l0aXZlIikgCgp0eXBlX2NvdW50IDwtIHZpcnVzZXMgJT4lIGNvdW50KHNlcXR5cGUsIHNpemVfY2xhc3MsIEluZGV4KQoKY29uZnVzaW9uX3Zpcl9jYWxsZWQkcGVyX3ZpcmFsIDwtIDAKCmZvciAoaSBpbiBjKDE6bnJvdyhjb25mdXNpb25fdmlyX2NhbGxlZCkpKSB7CiAgY29uZnVzaW9uX3Zpcl9jYWxsZWQkcGVyX3ZpcmFsW2ldIDwtIGNvbmZ1c2lvbl92aXJfY2FsbGVkJGNvdW50W2ldL3R5cGVfY291bnQkblt0eXBlX2NvdW50JHNlcXR5cGU9PWNvbmZ1c2lvbl92aXJfY2FsbGVkJHNlcXR5cGVbaV0gJiAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdHlwZV9jb3VudCRJbmRleD09Y29uZnVzaW9uX3Zpcl9jYWxsZWQkaW5kZXhbaV0gJgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0eXBlX2NvdW50JHNpemVfY2xhc3M9PWNvbmZ1c2lvbl92aXJfY2FsbGVkJHNpemVbaV1dKjEwMAp9Cgpjb25mdXNpb25fdmlyX2NhbGxlZCA8LSBjb25mdXNpb25fdmlyX2NhbGxlZCAlPiUgZ3JvdXBfYnkoc2VxdHlwZSwgc2l6ZSkgJT4lCiAgc3VtbWFyaXNlKG1lYW49bWVhbihwZXJfdmlyYWwpLCAKICAgICAgICAgICAgc2Q9c2QocGVyX3ZpcmFsKSkKYGBgCgpgYGB7cn0KZ2dwbG90KGNvbmZ1c2lvbl92aXJfY2FsbGVkLCBhZXMoeT1tZWFuLCB4PXNpemUsCiAgICAgICAgICAgICAgICAgICBmaWxsPXNlcXR5cGUsCiAgICAgICAgICAgICAgICAgICBjb2xvcj1zZXF0eXBlKSkgKwogIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5IiwgcG9zaXRpb249cG9zaXRpb25fZG9kZ2UoKSkgKwogIHRoZW1lX2xpZ2h0KCkgKwogIHRoZW1lKAogICAgcGFuZWwuZ3JpZC5tYWpvci55ID0gZWxlbWVudF9ibGFuaygpLAogICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9ibGFuaygpLAogICAgYXhpcy50aWNrcy55ID0gZWxlbWVudF9ibGFuaygpLAogICAgbGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsCiAgICBheGlzLnRleHQueT1lbGVtZW50X3RleHQoc2l6ZT0xNCksCiAgICBheGlzLnRleHQueD1lbGVtZW50X3RleHQoc2l6ZT0xNCksCiAgICBsZWdlbmQudGV4dD1lbGVtZW50X3RleHQoc2l6ZT0xMiksCiAgICBheGlzLnRpdGxlPWVsZW1lbnRfdGV4dChzaXplPTE2KSwKICApICsKICBnZW9tX2Vycm9yYmFyKGFlcyh5bWluPW1lYW4tc2QsIHltYXg9bWVhbitzZCksIHdpZHRoPS4yLAogICAgICAgICAgICAgICAgIHBvc2l0aW9uPXBvc2l0aW9uX2RvZGdlKC45KSkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKG5hbWU9IiIsCiAgICAgICAgICAgICAgICAgICAgIHZhbHVlcyA9IGFscGhhKHJldihwYWwoNikpLCAwLjUpKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKG5hbWU9IiIsCiAgICAgICAgICAgICAgICAgICAgIHZhbHVlcyA9IGFscGhhKHJldihwYWwoNikpLCAxKSkgKwogIHhsYWIoIkxlbmd0aCAoYnApIikgKwogIHlsYWIoIlNlcXVlbmNlcyBDYWxsZWQgVmlyYWwgKCUpIikgCmBgYAoKYGBge3J9CnZpcnVzZXMka2VlcF9zY29yZV92YiA8LSBnZXR0aW5nX3ZpcmFsX3NldF8xKHZpcnVzZXMsIGluY2x1ZGVfZGVlcHZpcmZpbmRlciA9IEYsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpbmNsdWRlX3ZpYnJhbnQgPSBULAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaW5jbHVkZV92aXJzb3J0ZXIyID0gRiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGluY2x1ZGVfdHVuaW5nX3ZpcmFsID0gRiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGluY2x1ZGVfdHVuaW5nX25vdF92aXJhbCA9IEYsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpbmNsdWRlX3ZpcnNvcnRlciA9IEYpCgp2aXJ1c2VzJGtlZXBfc2NvcmVfdmJfZHZmIDwtIGdldHRpbmdfdmlyYWxfc2V0XzEodmlydXNlcywgaW5jbHVkZV9kZWVwdmlyZmluZGVyID0gVCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGluY2x1ZGVfdmlicmFudCA9IFQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpbmNsdWRlX3ZpcnNvcnRlcjIgPSBGLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaW5jbHVkZV90dW5pbmdfdmlyYWwgPSBGLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaW5jbHVkZV90dW5pbmdfbm90X3ZpcmFsID0gRiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGluY2x1ZGVfdmlyc29ydGVyID0gRikKCnZpcnVzZXMka2VlcF9zY29yZV92Yl9kdmZfdnMyIDwtIGdldHRpbmdfdmlyYWxfc2V0XzEodmlydXNlcywgaW5jbHVkZV9kZWVwdmlyZmluZGVyID0gVCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGluY2x1ZGVfdmlicmFudCA9IFQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpbmNsdWRlX3ZpcnNvcnRlcjIgPSBULAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaW5jbHVkZV90dW5pbmdfdmlyYWwgPSBGLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaW5jbHVkZV90dW5pbmdfbm90X3ZpcmFsID0gRiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGluY2x1ZGVfdmlyc29ydGVyID0gRikKCnZpcnVzZXMka2VlcF9zY29yZV92Yl9kdmZfdnMyX3ZzIDwtIGdldHRpbmdfdmlyYWxfc2V0XzEodmlydXNlcywgaW5jbHVkZV9kZWVwdmlyZmluZGVyID0gVCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGluY2x1ZGVfdmlicmFudCA9IFQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpbmNsdWRlX3ZpcnNvcnRlcjIgPSBULAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaW5jbHVkZV90dW5pbmdfdmlyYWwgPSBGLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaW5jbHVkZV90dW5pbmdfbm90X3ZpcmFsID0gRiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGluY2x1ZGVfdmlyc29ydGVyID0gVCkKCnZpcnVzZXMka2VlcF9zY29yZV92Yl9kdmZfdnMyX3ZzX3R2IDwtIGdldHRpbmdfdmlyYWxfc2V0XzEodmlydXNlcywgaW5jbHVkZV9kZWVwdmlyZmluZGVyID0gVCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGluY2x1ZGVfdmlicmFudCA9IFQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpbmNsdWRlX3ZpcnNvcnRlcjIgPSBULAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaW5jbHVkZV90dW5pbmdfdmlyYWwgPSBULAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaW5jbHVkZV90dW5pbmdfbm90X3ZpcmFsID0gRiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGluY2x1ZGVfdmlyc29ydGVyID0gVCkKCnZpcnVzZXMka2VlcF9zY29yZV92Yl9kdmZfdnMyX3ZzX3R2X3RudiA8LSBnZXR0aW5nX3ZpcmFsX3NldF8xKHZpcnVzZXMsIGluY2x1ZGVfZGVlcHZpcmZpbmRlciA9IFQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpbmNsdWRlX3ZpYnJhbnQgPSBULAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaW5jbHVkZV92aXJzb3J0ZXIyID0gVCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGluY2x1ZGVfdHVuaW5nX3ZpcmFsID0gVCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGluY2x1ZGVfdHVuaW5nX25vdF92aXJhbCA9IFQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpbmNsdWRlX3ZpcnNvcnRlciA9IFQpCmBgYAoKIyBDb25zaWRlcmluZyBob3cgZWFjaCBtZXRob2QgY29udHJpYnV0ZXMgdG8gdGhlIGZpbmFsIHByZWRpY3Rpb24KYGBge3J9CnZpcnVzZXNfaGlnaCA8LSB2aXJ1c2VzW3ZpcnVzZXMka2VlcF9zY29yZV92Yl9kdmZfdnMyX3ZzX3R2Pj0xLF0gCnZpcnVzZXNfaGlnaF9tb2QgPC0gdmlydXNlc19oaWdoICU+JSBzZWxlY3Qoa2VlcF9zY29yZV92YixrZWVwX3Njb3JlX3ZiX2R2ZiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAga2VlcF9zY29yZV92Yl9kdmZfdnMyLCBrZWVwX3Njb3JlX3ZiX2R2Zl92czJfdnMsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGtlZXBfc2NvcmVfdmJfZHZmX3ZzMl92c190diwga2VlcF9zY29yZV92Yl9kdmZfdnMyX3ZzX3R2X3RudikKI3ZpcnVzZXNfaGlnaF9tb2QgPC0gYXBwbHkodmlydXNlc19oaWdoX21vZCwgYygxLDIpLCBmdW5jdGlvbih4KSB7aWYgKHggPj0gMSkge3ggPC0gMX0gZWxzZSB7eCA8LSAwfX0pCnZpcnVzZXNfaGlnaF9tb2QgPC0gYXNfdGliYmxlKHZpcnVzZXNfaGlnaF9tb2QpCgoKYGBgCgpgYGB7cn0Kc21fbSA8LSByZXNoYXBlMjo6bWVsdCh2aXJ1c2VzX2hpZ2hfbW9kKQpjb2xuYW1lcyhzbV9tKSA8LSBjKCJtZXRob2QiLCAidmlyYWxfc2NvcmUiKQpgYGAKCmBgYHtyfQpzbV9tIDwtIHNtX21bc21fbSR2aXJhbF9zY29yZT4wLF0KCnNtX20kc2NvcmUgPC0gc21fbSR2aXJhbF9zY29yZQoKc21fbSRzY29yZVtzbV9tJHZpcmFsX3Njb3JlPT0wLjVdIDwtICIwLjUiCnNtX20kc2NvcmVbc21fbSR2aXJhbF9zY29yZT49MV0gPC0gIjEiCnNtX20kc2NvcmVbc21fbSR2aXJhbF9zY29yZT49Ml0gPC0gIjIiCnNtX20kc2NvcmVbc21fbSR2aXJhbF9zY29yZT49M10gPC0gIjMiCnNtX20kc2NvcmVbc21fbSR2aXJhbF9zY29yZT49NF0gPC0gIjQiCnNtX20kc2NvcmVbc21fbSR2aXJhbF9zY29yZT49NV0gPC0gIjUiCgpzbV9tJHNjb3JlIDwtIGZhY3RvcihzbV9tJHNjb3JlLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzPWMoIjAuNSIsICIxIiwgIjIiLCIzIiwiNCIsIjUiKSkKYGBgCgoKYGBge3J9CmdncGxvdChzbV9tLCBhZXMoeD1tZXRob2QsIHk9c2NvcmUsCiAgICAgICAgICAgICAgICAgICBmaWxsPXNjb3JlKSkgKwogIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5IikgKwogIHRoZW1lX2xpZ2h0KCkgKwogIGNvb3JkX2ZsaXAoKSArCiAgdGhlbWUoCiAgICBwYW5lbC5ncmlkLm1ham9yLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X2JsYW5rKCksCiAgICBsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIgogICkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKG5hbWUgPSAnVmlyYWwgU2NvcmUnLAogICAgICAgICAgICAgICAgICAgICB2YWx1ZXMgPSBhbHBoYShjKHZpcmlkaXMoNikpLCAxKSkgKwogIHhsYWIoIiIpICsKICB5bGFiKCJOdW1iZXIgb2YgU2VxdWVuY2VzIikgKwogIGNvb3JkX2ZsaXAoKQpgYGAKIyMgVmlzdWFsaXppbmcgY29uZnVzaW9uIG1hdHJpeCBieSBudW1iZXIgb2YgdG9vbHMKCgpgYGB7cn0KdmlydXNlcyRrZWVwX3Njb3JlX3Zpc3VhbGl6ZSA8LSB2aXJ1c2VzJGtlZXBfc2NvcmVfaGlnaF9NQ0MKdmlydXNlcyRrZWVwX3Njb3JlX3Zpc3VhbGl6ZVt2aXJ1c2VzJGtlZXBfc2NvcmVfaGlnaF9NQ0M+MV0gPC0gIj4gMSIKdmlydXNlcyRrZWVwX3Njb3JlX3Zpc3VhbGl6ZVt2aXJ1c2VzJGtlZXBfc2NvcmVfaGlnaF9NQ0M9PTFdIDwtICIxIgp2aXJ1c2VzJGtlZXBfc2NvcmVfdmlzdWFsaXplW3ZpcnVzZXMka2VlcF9zY29yZV9oaWdoX01DQz09MC41XSA8LSAiMC41Igp2aXJ1c2VzJGtlZXBfc2NvcmVfdmlzdWFsaXplW3ZpcnVzZXMka2VlcF9zY29yZV9oaWdoX01DQz09MF0gPC0gIjAiCnZpcnVzZXMka2VlcF9zY29yZV92aXN1YWxpemVbdmlydXNlcyRrZWVwX3Njb3JlX2hpZ2hfTUNDPT0tMC41XSA8LSAiLTAuNSIKdmlydXNlcyRrZWVwX3Njb3JlX3Zpc3VhbGl6ZVt2aXJ1c2VzJGtlZXBfc2NvcmVfaGlnaF9NQ0M9PS0xXSA8LSAiLTEiCnZpcnVzZXMka2VlcF9zY29yZV92aXN1YWxpemVbdmlydXNlcyRrZWVwX3Njb3JlX2hpZ2hfTUNDPD0tMV0gPC0gIjwgLTEiCgp2aXJ1c2VzJGtlZXBfc2NvcmVfdmlzdWFsaXplIDwtIGZhY3Rvcih2aXJ1c2VzJGtlZXBfc2NvcmVfdmlzdWFsaXplLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzPWMoIjwgLTEiLCAiLTEiLCAiLTAuNSIsICIwIiwgIjAuNSIsIjEiLCAiPiAxIikpCiN2aXJ1c2VzJGtlZXBfc2NvcmVfdmlzdWFsaXplIDwtIGZhY3Rvcih2aXJ1c2VzJGtlZXBfc2NvcmVfdmlzdWFsaXplLCAKIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVscz1jKCLiiaQgMCIsICLiiaQgMCIsICLiiaQgMCIsICIwLjUiLCIxIiwgIj4gMSIpKQpgYGAKCmBgYHtyfQpsZXZlbHMoZmFjdG9yKHZpcnVzZXMka2VlcF9zY29yZV92aXN1YWxpemUpKQpgYGAKCmBgYHtyfQpnZ3Bsb3QodmlydXNlcywgYWVzKHg9YXMuZmFjdG9yKEluZGV4KSwKICAgICAgICAgICAgICAgICAgIGZpbGw9a2VlcF9zY29yZV92aXN1YWxpemUsIGNvbG9yPWtlZXBfc2NvcmVfdmlzdWFsaXplKSkgKwogIGdlb21fYmFyKHN0YXQ9ImNvdW50IiwgcG9zaXRpb249InN0YWNrIikgKwogIHRoZW1lX2xpZ2h0KCkgKwogIGNvb3JkX2ZsaXAoKSArCiAgdGhlbWUoCiAgICBwYW5lbC5ncmlkLm1ham9yLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X2JsYW5rKCksCiAgICBheGlzLnRpY2tzLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICBsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwKICAgIGF4aXMudGV4dC55PWVsZW1lbnRfdGV4dChzaXplPTE0KSwKICAgIGF4aXMudGV4dC54PWVsZW1lbnRfdGV4dChzaXplPTE0KSwKICAgIGxlZ2VuZC50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTEyKSwKICAgIGF4aXMudGl0bGU9ZWxlbWVudF90ZXh0KHNpemU9MTYpCiAgKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKG5hbWUgPSAnVmlyYWwgU2NvcmUnLAogICAgICAgICAgICAgICAgICAgICB2YWx1ZXMgPSBhbHBoYShjKHZpcmlkaXMoNikpLCAxKSkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKG5hbWUgPSAnVmlyYWwgU2NvcmUnLAogICAgICAgICAgICAgICAgICAgICB2YWx1ZXMgPSBhbHBoYShjKHZpcmlkaXMoNikpLCAwLjUpKSArCiAgeGxhYigiSW5kZXgiKSArCiAgeWxhYigiU2VxdWVuY2UgQ291bnQiKSArCiAgZmFjZXRfd3JhcCh+Y29uZnVzaW9uX21hdHJpeF9oaWdoX01DQywgc2NhbGVzID0gImZyZWUiKQoKYGBgCgoKCiMjIGFsbCA2IHRvb2xzIGV4YW1wbGUKYGBge3J9CnZpcnVzZXMka2VlcF9zY29yZV9hbGwgPC0gZ2V0dGluZ192aXJhbF9zZXRfMSh2aXJ1c2VzLCBpbmNsdWRlX2RlZXB2aXJmaW5kZXIgPSBULAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaW5jbHVkZV92aWJyYW50ID0gVCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGluY2x1ZGVfdmlyc29ydGVyMiA9IFQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpbmNsdWRlX3R1bmluZ192aXJhbCA9IFQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpbmNsdWRlX3R1bmluZ19ub3RfdmlyYWwgPSBULAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaW5jbHVkZV92aXJzb3J0ZXIgPSBUKQpgYGAKCgpgYGB7cn0KdmlydXNlcyRjb25mdXNpb25fbWF0cml4X2FsbCA8LSAidHJ1ZSBuZWdhdGl2ZSIKdmlydXNlcyRjb25mdXNpb25fbWF0cml4X2FsbFt2aXJ1c2VzJHNlcXR5cGU9PSJ2aXJ1cyIgJiB2aXJ1c2VzJGtlZXBfc2NvcmVfYWxsPDFdIDwtICJmYWxzZSBuZWdhdGl2ZSIKdmlydXNlcyRjb25mdXNpb25fbWF0cml4X2FsbFt2aXJ1c2VzJHNlcXR5cGU9PSJ2aXJ1cyIgJiB2aXJ1c2VzJGtlZXBfc2NvcmVfYWxsPj0xXSA8LSAidHJ1ZSBwb3NpdGl2ZSIKdmlydXNlcyRjb25mdXNpb25fbWF0cml4X2FsbFt2aXJ1c2VzJHNlcXR5cGUhPSJ2aXJ1cyIgJiB2aXJ1c2VzJGtlZXBfc2NvcmVfYWxsPj0xXSA8LSAiZmFsc2UgcG9zaXRpdmUiCmBgYAoKdmlzdWFsaXppbmcgY29uZnVzaW9uIG1hdHJpeCBieSB0YXhhCmBgYHtyfQpjb25mdXNpb25fYnlfdGF4YSA8LSBtZWx0KHRhYmxlKHZpcnVzZXMkY29uZnVzaW9uX21hdHJpeF9hbGwsIHZpcnVzZXMkc2VxdHlwZSwgdmlydXNlcyRJbmRleCkpCmNvbG5hbWVzKGNvbmZ1c2lvbl9ieV90YXhhKSA8LSBjKCJjb25mdXNpb25fbWF0cml4IiwgInNlcXR5cGUiLCJJbmRleCIsICJjb3VudCIpCmBgYAoKYGBge3J9CnRhYmxlKHZpcnVzZXMkY29uZnVzaW9uX21hdHJpeF9hbGwpCgpsZW5ndGgoZ3JlcCgidHJ1ZSIsIHZpcnVzZXMkY29uZnVzaW9uX21hdHJpeF9hbGwpKS9ucm93KHZpcnVzZXMpCmBgYAoKYGBge3J9Cmxlbmd0aChncmVwKCJ0cnVlIHBvc2l0aXZlIiwgdmlydXNlcyRjb25mdXNpb25fbWF0cml4X2FsbCkpL2xlbmd0aChncmVwKCJ2aXJ1cyIsIHZpcnVzZXMkc2VxdHlwZSkpCmBgYAoKYGBge3J9CnBhbCA8LSBnZ3RoZW1lczo6dGFibGVhdV9jb2xvcl9wYWwocGFsZXR0ZT0iVGFibGVhdSAxMCIsIHR5cGU9InJlZ3VsYXIiKQpgYGAKCmBgYHtyfQpnZ3Bsb3QoY29uZnVzaW9uX2J5X3RheGEsIGFlcyh4PWNvdW50LCB5PWFzLmZhY3RvcihJbmRleCksCiAgICAgICAgICAgICAgICAgICBmaWxsPWNvbmZ1c2lvbl9tYXRyaXgsCiAgICAgICAgICAgICAgICAgICBjb2xvcj1jb25mdXNpb25fbWF0cml4KSkgKwogIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5IikgKwogIHRoZW1lX2xpZ2h0KCkgKwogIHRoZW1lKAogICAgcGFuZWwuZ3JpZC5tYWpvci55ID0gZWxlbWVudF9ibGFuaygpLAogICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9ibGFuaygpLAogICAgYXhpcy50aWNrcy55ID0gZWxlbWVudF9ibGFuaygpLAogICAgbGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsCiAgICBheGlzLnRleHQueT1lbGVtZW50X3RleHQoc2l6ZT0xNCksCiAgICBheGlzLnRleHQueD1lbGVtZW50X3RleHQoc2l6ZT0xNCksCiAgICBsZWdlbmQudGV4dD1lbGVtZW50X3RleHQoc2l6ZT0xMiksCiAgICBheGlzLnRpdGxlPWVsZW1lbnRfdGV4dChzaXplPTE2KSwKICApICsKICBzY2FsZV9maWxsX21hbnVhbChuYW1lPSIiLAogICAgICAgICAgICAgICAgICAgICB2YWx1ZXMgPSBhbHBoYShyZXYocGFsKDQpKSwgMC41KSwKICAgICAgICAgICAgICAgICAgICBsYWJlbHM9YygiZmFsc2UgbmVnYXRpdmUiLCAiZmFsc2UgcG9zaXRpdmUiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHJ1ZSBuZWdhdGl2ZSIsICJ0cnVlIHBvc2l0aXZlIikpICsKICBzY2FsZV9jb2xvcl9tYW51YWwobmFtZT0iIiwKICAgICAgICAgICAgICAgICAgICAgdmFsdWVzID0gYWxwaGEocmV2KHBhbCg0KSksIDEpLAogICAgICAgICAgICAgICAgICAgIGxhYmVscz1jKCJmYWxzZSBuZWdhdGl2ZSIsICJmYWxzZSBwb3NpdGl2ZSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0cnVlIG5lZ2F0aXZlIiwgInRydWUgcG9zaXRpdmUiKSkgKwogIHhsYWIoIk51bWJlciBvZiBTZXF1ZW5jZXMiKSArCiAgeWxhYigiIikgKyAKICBmYWNldF93cmFwKH5zZXF0eXBlLCBzY2FsZXMgPSAiZnJlZSIpICsKICBjb29yZF9mbGlwKCkKYGBgCgoKIyMgaGlnaCByZWNhbGwgZXhhbXBsZQpgYGB7cn0KdmlydXNlcyRrZWVwX3Njb3JlX2hpZ2hfcmVjYWxsIDwtIGdldHRpbmdfdmlyYWxfc2V0XzEodmlydXNlcywgaW5jbHVkZV9kZWVwdmlyZmluZGVyID0gVCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGluY2x1ZGVfdmlicmFudCA9IFQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpbmNsdWRlX3ZpcnNvcnRlcjIgPSBULAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaW5jbHVkZV90dW5pbmdfdmlyYWwgPSBULAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaW5jbHVkZV90dW5pbmdfbm90X3ZpcmFsID0gRiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGluY2x1ZGVfdmlyc29ydGVyID0gVCkKYGBgCgoKYGBge3J9CnZpcnVzZXMkY29uZnVzaW9uX21hdHJpeF9oaWdoX3JlY2FsbCA8LSAidHJ1ZSBuZWdhdGl2ZSIKdmlydXNlcyRjb25mdXNpb25fbWF0cml4X2hpZ2hfcmVjYWxsW3ZpcnVzZXMkc2VxdHlwZT09InZpcnVzIiAmIHZpcnVzZXMka2VlcF9zY29yZV9oaWdoX3JlY2FsbDwxXSA8LSAiZmFsc2UgbmVnYXRpdmUiCnZpcnVzZXMkY29uZnVzaW9uX21hdHJpeF9oaWdoX3JlY2FsbFt2aXJ1c2VzJHNlcXR5cGU9PSJ2aXJ1cyIgJiB2aXJ1c2VzJGtlZXBfc2NvcmVfaGlnaF9yZWNhbGw+PTFdIDwtICJ0cnVlIHBvc2l0aXZlIgp2aXJ1c2VzJGNvbmZ1c2lvbl9tYXRyaXhfaGlnaF9yZWNhbGxbdmlydXNlcyRzZXF0eXBlIT0idmlydXMiICYgdmlydXNlcyRrZWVwX3Njb3JlX2hpZ2hfcmVjYWxsPj0xXSA8LSAiZmFsc2UgcG9zaXRpdmUiCmBgYAoKCnZpc3VhbGl6aW5nIGNvbmZ1c2lvbiBtYXRyaXggYnkgdGF4YQpgYGB7cn0KY29uZnVzaW9uX2J5X3RheGEgPC0gbWVsdCh0YWJsZSh2aXJ1c2VzJGNvbmZ1c2lvbl9tYXRyaXhfaGlnaF9yZWNhbGwsIHZpcnVzZXMkc2VxdHlwZSwgdmlydXNlcyRJbmRleCkpCmNvbG5hbWVzKGNvbmZ1c2lvbl9ieV90YXhhKSA8LSBjKCJjb25mdXNpb25fbWF0cml4IiwgInNlcXR5cGUiLCJJbmRleCIsICJjb3VudCIpCmBgYAoKCgpgYGB7cn0KcGFsIDwtIGdndGhlbWVzOjp0YWJsZWF1X2NvbG9yX3BhbChwYWxldHRlPSJUYWJsZWF1IDEwIiwgdHlwZT0icmVndWxhciIpCmBgYAoKYGBge3J9CnAyIDwtIGdncGxvdChjb25mdXNpb25fYnlfdGF4YSwgYWVzKHg9Y291bnQsIHk9YXMuZmFjdG9yKEluZGV4KSwKICAgICAgICAgICAgICAgICAgIGZpbGw9Y29uZnVzaW9uX21hdHJpeCwKICAgICAgICAgICAgICAgICAgIGNvbG9yPWNvbmZ1c2lvbl9tYXRyaXgpKSArCiAgZ2VvbV9iYXIoc3RhdD0iaWRlbnRpdHkiKSArCiAgdGhlbWVfbGlnaHQoKSArCiAgdGhlbWUoCiAgICBwYW5lbC5ncmlkLm1ham9yLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X2JsYW5rKCksCiAgICBheGlzLnRpY2tzLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICBsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwKICAgIGF4aXMudGV4dC55PWVsZW1lbnRfdGV4dChzaXplPTE0KSwKICAgIGF4aXMudGV4dC54PWVsZW1lbnRfdGV4dChzaXplPTE0KSwKICAgIGxlZ2VuZC50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTEyKSwKICAgIGF4aXMudGl0bGU9ZWxlbWVudF90ZXh0KHNpemU9MTYpLAogICkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKG5hbWU9IiIsCiAgICAgICAgICAgICAgICAgICAgIHZhbHVlcyA9IGFscGhhKHJldihwYWwoNCkpLCAwLjUpLAogICAgICAgICAgICAgICAgICAgIGxhYmVscz1jKCJmYWxzZSBuZWdhdGl2ZSIsICJmYWxzZSBwb3NpdGl2ZSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0cnVlIG5lZ2F0aXZlIiwgInRydWUgcG9zaXRpdmUiKSkgKwogIHNjYWxlX2NvbG9yX21hbnVhbChuYW1lPSIiLAogICAgICAgICAgICAgICAgICAgICB2YWx1ZXMgPSBhbHBoYShyZXYocGFsKDQpKSwgMSksCiAgICAgICAgICAgICAgICAgICAgbGFiZWxzPWMoImZhbHNlIG5lZ2F0aXZlIiwgImZhbHNlIHBvc2l0aXZlIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInRydWUgbmVnYXRpdmUiLCAidHJ1ZSBwb3NpdGl2ZSIpKSArCiAgeGxhYigiTnVtYmVyIG9mIFNlcXVlbmNlcyIpICsKICB5bGFiKCIiKSArIAogIGZhY2V0X3dyYXAofnNlcXR5cGUsIHNjYWxlcyA9ICJmcmVlIikgKwogIGNvb3JkX2ZsaXAoKQpwMgpgYGAKCmFjY3VyYWN5OgpgYGB7cn0KbGVuZ3RoKGdyZXAoInRydWUiLCB2aXJ1c2VzJGNvbmZ1c2lvbl9tYXRyaXhfaGlnaF9yZWNhbGwpKS9ucm93KHZpcnVzZXMpCmBgYAowLjg4NwoKcmVjYWxsCmBgYHtyfQpsZW5ndGgoZ3JlcCgidHJ1ZSBwb3NpdGl2ZSIsIHZpcnVzZXMkY29uZnVzaW9uX21hdHJpeF9oaWdoX3JlY2FsbCkpL2xlbmd0aChncmVwKCJ2aXJ1cyIsIHZpcnVzZXMkc2VxdHlwZSkpCmBgYApyZWNvdmVyIGFsbW9zdCBhbGwgb2YgdGhlIHZpcnVzZXMgdGhpcyB3YXksIGJ1dCBtb3JlIHByb3Rpc3QgY29udGFtaW5hdGlvbgoKMC45NjAKCgoKCgoKCgoKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKRXh0cmEgU3R1ZmYKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgpgYGB7cn0KZ2dwbG90KHZpcnVzZXMsIGFlcyh4PWNoZWNrdl9sZW5ndGgsIHk9a2VlcF9zY29yZV9oaWdoX01DQywKICAgICAgICAgICAgICAgICAgIGZpbGw9Y29uZnVzaW9uX21hdHJpeF9oaWdoX01DQywKICAgICAgICAgICAgICAgICAgIGNvbG9yPWNvbmZ1c2lvbl9tYXRyaXhfaGlnaF9NQ0MpKSArCiAgZ2VvbV9wb2ludChzdGF0PSJpZGVudGl0eSIsIHNoYXBlPTIxKSArCiAgdGhlbWVfbGlnaHQoKSArCiAgdGhlbWUoCiAgICBwYW5lbC5ncmlkLm1ham9yLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X2JsYW5rKCksCiAgICBheGlzLnRpY2tzLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICBsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwKICAgIGF4aXMudGV4dC55PWVsZW1lbnRfdGV4dChzaXplPTE0KSwKICAgIGF4aXMudGV4dC54PWVsZW1lbnRfdGV4dChzaXplPTE0KSwKICAgIGxlZ2VuZC50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTEyKSwKICAgIGF4aXMudGl0bGU9ZWxlbWVudF90ZXh0KHNpemU9MTYpLAogICkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKG5hbWU9IiIsCiAgICAgICAgICAgICAgICAgICAgIHZhbHVlcyA9IGFscGhhKHJldihwYWwoNCkpLCAwLjUpLAogICAgICAgICAgICAgICAgICAgIGxhYmVscz1jKCJmYWxzZSBuZWdhdGl2ZSIsICJmYWxzZSBwb3NpdGl2ZSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0cnVlIG5lZ2F0aXZlIiwgInRydWUgcG9zaXRpdmUiKSkgKwogIHNjYWxlX2NvbG9yX21hbnVhbChuYW1lPSIiLAogICAgICAgICAgICAgICAgICAgICB2YWx1ZXMgPSBhbHBoYShyZXYocGFsKDQpKSwgMSksCiAgICAgICAgICAgICAgICAgICAgbGFiZWxzPWMoImZhbHNlIG5lZ2F0aXZlIiwgImZhbHNlIHBvc2l0aXZlIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInRydWUgbmVnYXRpdmUiLCAidHJ1ZSBwb3NpdGl2ZSIpKSArCiAgeGxhYigiU2VxdWVuY2UgTGVuZ3RoIChicCkiKSArCiAgeWxhYigiUGlwZWxpbmUgVmlyYWwgU2NvcmUiKSArIAogIGZhY2V0X3dyYXAofnNlcXR5cGUpICsgCiAgc2NhbGVfeF9sb2cxMCgpCmBgYAoKCmBgYHtyfQpnZ3Bsb3QodmlydXNlcywgYWVzKHg9Y2hlY2t2X2NvbXBsZXRlbmVzcywgeT1oYWxsbWFyaywKICAgICAgICAgICAgICAgICAgIGZpbGw9Y29uZnVzaW9uX21hdHJpeF9oaWdoX3JlY2FsbCwKICAgICAgICAgICAgICAgICAgIGNvbG9yPWNvbmZ1c2lvbl9tYXRyaXhfaGlnaF9yZWNhbGwpKSArCiAgZ2VvbV9wb2ludChzdGF0PSJpZGVudGl0eSIsIHNoYXBlPTIxKSArCiAgdGhlbWVfbGlnaHQoKSArCiAgdGhlbWUoCiAgICBwYW5lbC5ncmlkLm1ham9yLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X2JsYW5rKCksCiAgICBheGlzLnRpY2tzLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICBsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwKICAgIGF4aXMudGV4dC55PWVsZW1lbnRfdGV4dChzaXplPTE0KSwKICAgIGF4aXMudGV4dC54PWVsZW1lbnRfdGV4dChzaXplPTE0KSwKICAgIGxlZ2VuZC50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTEyKSwKICAgIGF4aXMudGl0bGU9ZWxlbWVudF90ZXh0KHNpemU9MTYpLAogICkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKG5hbWU9IiIsCiAgICAgICAgICAgICAgICAgICAgIHZhbHVlcyA9IGFscGhhKHJldihwYWwoNCkpLCAwLjUpLAogICAgICAgICAgICAgICAgICAgIGxhYmVscz1jKCJmYWxzZSBuZWdhdGl2ZSIsICJmYWxzZSBwb3NpdGl2ZSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0cnVlIG5lZ2F0aXZlIiwgInRydWUgcG9zaXRpdmUiKSkgKwogIHNjYWxlX2NvbG9yX21hbnVhbChuYW1lPSIiLAogICAgICAgICAgICAgICAgICAgICB2YWx1ZXMgPSBhbHBoYShyZXYocGFsKDQpKSwgMSksCiAgICAgICAgICAgICAgICAgICAgbGFiZWxzPWMoImZhbHNlIG5lZ2F0aXZlIiwgImZhbHNlIHBvc2l0aXZlIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInRydWUgbmVnYXRpdmUiLCAidHJ1ZSBwb3NpdGl2ZSIpKSArCiAgeGxhYigiQ2hlY2tWIENvbXBsZXRlbmVzcyIpICsKICB5bGFiKCJOdW1iZXIgb2YgSGFsbG1hcmsgR2VuZXMiKSArIAogIGZhY2V0X3dyYXAofnNlcXR5cGUpICsgCiAgc2NhbGVfeF9sb2cxMCgpCmBgYAoKYGBge3J9CmdncGxvdCh2aXJ1c2VzLCBhZXMoeD1jaGVja3ZfY29tcGxldGVuZXNzLCB5PWtlZXBfc2NvcmVfaGlnaF9NQ0MsCiAgICAgICAgICAgICAgICAgICBmaWxsPWNvbmZ1c2lvbl9tYXRyaXhfaGlnaF9yZWNhbGwsCiAgICAgICAgICAgICAgICAgICBjb2xvcj1jb25mdXNpb25fbWF0cml4X2hpZ2hfcmVjYWxsKSkgKwogIGdlb21fcG9pbnQoc3RhdD0iaWRlbnRpdHkiLCBzaGFwZT0yMSkgKwogIHRoZW1lX2xpZ2h0KCkgKwogIHRoZW1lKAogICAgcGFuZWwuZ3JpZC5tYWpvci55ID0gZWxlbWVudF9ibGFuaygpLAogICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9ibGFuaygpLAogICAgYXhpcy50aWNrcy55ID0gZWxlbWVudF9ibGFuaygpLAogICAgbGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsCiAgICBheGlzLnRleHQueT1lbGVtZW50X3RleHQoc2l6ZT0xNCksCiAgICBheGlzLnRleHQueD1lbGVtZW50X3RleHQoc2l6ZT0xNCksCiAgICBsZWdlbmQudGV4dD1lbGVtZW50X3RleHQoc2l6ZT0xMiksCiAgICBheGlzLnRpdGxlPWVsZW1lbnRfdGV4dChzaXplPTE2KSwKICApICsKICBzY2FsZV9maWxsX21hbnVhbChuYW1lPSIiLAogICAgICAgICAgICAgICAgICAgICB2YWx1ZXMgPSBhbHBoYShyZXYocGFsKDQpKSwgMC41KSwKICAgICAgICAgICAgICAgICAgICBsYWJlbHM9YygiZmFsc2UgbmVnYXRpdmUiLCAiZmFsc2UgcG9zaXRpdmUiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHJ1ZSBuZWdhdGl2ZSIsICJ0cnVlIHBvc2l0aXZlIikpICsKICBzY2FsZV9jb2xvcl9tYW51YWwobmFtZT0iIiwKICAgICAgICAgICAgICAgICAgICAgdmFsdWVzID0gYWxwaGEocmV2KHBhbCg0KSksIDEpLAogICAgICAgICAgICAgICAgICAgIGxhYmVscz1jKCJmYWxzZSBuZWdhdGl2ZSIsICJmYWxzZSBwb3NpdGl2ZSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0cnVlIG5lZ2F0aXZlIiwgInRydWUgcG9zaXRpdmUiKSkgKwogIHhsYWIoIkNoZWNrViBDb21wbGV0ZW5lc3MiKSArCiAgeWxhYigiUGlwZWxpbmUgVmlyYWwgU2NvcmUiKSArIAogIGZhY2V0X3dyYXAofnNlcXR5cGUpICsgCiAgc2NhbGVfeF9sb2cxMCgpCmBgYAoKYGBge3J9CmdncGxvdCh2aXJ1c2VzLCBhZXMoeD1jb25mdXNpb25fbWF0cml4X2hpZ2hfcmVjYWxsLCB5PWNoZWNrdl9sZW5ndGgsCiAgICAgICAgICAgICAgICAgICBmaWxsPWNvbmZ1c2lvbl9tYXRyaXhfaGlnaF9yZWNhbGwsCiAgICAgICAgICAgICAgICAgICBjb2xvcj1jb25mdXNpb25fbWF0cml4X2hpZ2hfcmVjYWxsKSkgKwogIGdlb21fYm94cGxvdCgpICsKICB0aGVtZV9saWdodCgpICsKICB0aGVtZSgKICAgIHBhbmVsLmdyaWQubWFqb3IueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIGF4aXMudGlja3MueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iLAogICAgYXhpcy50ZXh0Lnk9ZWxlbWVudF90ZXh0KHNpemU9MTQpLAogICAgYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KHNpemU9MTQpLAogICAgbGVnZW5kLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9MTIpLAogICAgYXhpcy50aXRsZT1lbGVtZW50X3RleHQoc2l6ZT0xNiksCiAgKSArCiAgc2NhbGVfZmlsbF9tYW51YWwobmFtZT0iIiwKICAgICAgICAgICAgICAgICAgICAgdmFsdWVzID0gYWxwaGEocmV2KHBhbCg0KSksIDAuNSksCiAgICAgICAgICAgICAgICAgICAgbGFiZWxzPWMoImZhbHNlIG5lZ2F0aXZlIiwgImZhbHNlIHBvc2l0aXZlIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInRydWUgbmVnYXRpdmUiLCAidHJ1ZSBwb3NpdGl2ZSIpKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKG5hbWU9IiIsCiAgICAgICAgICAgICAgICAgICAgIHZhbHVlcyA9IGFscGhhKHJldihwYWwoNCkpLCAxKSwKICAgICAgICAgICAgICAgICAgICBsYWJlbHM9YygiZmFsc2UgbmVnYXRpdmUiLCAiZmFsc2UgcG9zaXRpdmUiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHJ1ZSBuZWdhdGl2ZSIsICJ0cnVlIHBvc2l0aXZlIikpICsKICB4bGFiKCJTZXF1ZW5jZSBMZW5ndGggKGJwKSIpICsKICB5bGFiKCJQaXBlbGluZSBWaXJhbCBTY29yZSIpICsKICBzY2FsZV95X2xvZzEwKCkKYGBgCgoKCmxvb2tpbmcgYXQgZmFsc2UgbmVnYXRpdmVzCmBgYHtyfQp2aXJ1c2VzX2ZhbHNlX25lZ3MgPC0gdmlydXNlc1sodmlydXNlcyRzZXF0eXBlPT0idmlydXMiICYgdmlydXNlcyRrZWVwX3Njb3JlX2hpZ2hfcmVjYWxsPDEpLF0KYGBgCgpsb29raW5nIGF0IHByb3Rpc3RzIGNhbGxpbmcgdmlyYWwKYGBge3J9CnZpcnVzZXNfZmFsc2VfcG9zX3Byb3Rpc3RzIDwtIHZpcnVzZXNbKHZpcnVzZXMkc2VxdHlwZT09InByb3Rpc3QiICYgdmlydXNlcyRrZWVwX3Njb3JlX2hpZ2hfcmVjYWxsPj0xKSxdCmBgYAoKCgoKCgoKCgoKCiMgQ29uc2lkZXJpbmcgaG93IGVhY2ggbWV0aG9kIGNvbnRyaWJ1dGVzIHRvIHRoZSBmaW5hbCBwcmVkaWN0aW9uIChoaWdoIE1DQykKCmBgYHtyfQp2aXJ1c2VzJGtlZXBfc2NvcmVfdmIgPC0gZ2V0dGluZ192aXJhbF9zZXRfMSh2aXJ1c2VzLCBpbmNsdWRlX2RlZXB2aXJmaW5kZXIgPSBGLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaW5jbHVkZV92aWJyYW50ID0gVCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGluY2x1ZGVfdmlyc29ydGVyMiA9IEYsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpbmNsdWRlX3R1bmluZ192aXJhbCA9IEYsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpbmNsdWRlX3R1bmluZ19ub3RfdmlyYWwgPSBGLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaW5jbHVkZV92aXJzb3J0ZXIgPSBGKQoKdmlydXNlcyRrZWVwX3Njb3JlX3ZiX3R2IDwtIGdldHRpbmdfdmlyYWxfc2V0XzEodmlydXNlcywgaW5jbHVkZV9kZWVwdmlyZmluZGVyID0gRiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGluY2x1ZGVfdmlicmFudCA9IFQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpbmNsdWRlX3ZpcnNvcnRlcjIgPSBULAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaW5jbHVkZV90dW5pbmdfdmlyYWwgPSBULAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaW5jbHVkZV90dW5pbmdfbm90X3ZpcmFsID0gRiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGluY2x1ZGVfdmlyc29ydGVyID0gRikKYGBgCgpgYGB7cn0KdmlydXNlc19oaWdoIDwtIHZpcnVzZXNbdmlydXNlcyRrZWVwX3Njb3JlX3ZiX3R2Pj0xLF0gI3VuY29tbWVudCB0aGlzIGxpbmUgaWYgd2FudCB0byB1c2UgYWxsIDYgdG9vbHMKdmlydXNlc19oaWdoX21vZCA8LSB2aXJ1c2VzX2hpZ2ggJT4lIHNlbGVjdChrZWVwX3Njb3JlX3ZiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBrZWVwX3Njb3JlX3ZiX3R2KQojdmlydXNlc19oaWdoX21vZCA8LSBhcHBseSh2aXJ1c2VzX2hpZ2hfbW9kLCBjKDEsMiksIGZ1bmN0aW9uKHgpIHtpZiAoeCA+PSAxKSB7eCA8LSAxfSBlbHNlIHt4IDwtIDB9fSkKdmlydXNlc19oaWdoX21vZCA8LSBhc190aWJibGUodmlydXNlc19oaWdoX21vZCkKCgpgYGAKCmBgYHtyfQpzbV9tIDwtIHJlc2hhcGUyOjptZWx0KHZpcnVzZXNfaGlnaF9tb2QpCmNvbG5hbWVzKHNtX20pIDwtIGMoIm1ldGhvZCIsICJzY29yZSIpCmBgYAoKYGBge3J9CmdncGxvdChzbV9tLCBhZXMoeD1tZXRob2QsIHk9c2NvcmUsCiAgICAgICAgICAgICAgICAgICBmaWxsPWFzLmZhY3RvcihzY29yZSkpKSArCiAgZ2VvbV9iYXIoc3RhdD0iaWRlbnRpdHkiKSArCiAgdGhlbWVfbGlnaHQoKSArCiAgY29vcmRfZmxpcCgpICsKICB0aGVtZSgKICAgIHBhbmVsLmdyaWQubWFqb3IueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIGF4aXMudGlja3MueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iCiAgKSArCiAgc2NhbGVfZmlsbF9tYW51YWwobmFtZSA9ICdOdW1iZXIgb2YgTWV0aG9kcycsCiAgICAgICAgICAgICAgICAgICAgIHZhbHVlcyA9IGFscGhhKGModmlyaWRpcygxNCkpLCAxKSkgKwogIHhsYWIoIlByaW1hcnkgTWV0aG9kIikgKwogIHlsYWIoIkNvdW50IG9mIFZpcmFsIENvbnRpZ3MiKSArCiAgY29vcmRfZmxpcCgpCmBgYAoKCgoKIyBST0MgCmBgYHtyfQpsaWJyYXJ5KHBST0MpCmBgYAoKYGBge3J9CnZpcnVzZXMkdHJ1ZXBvc2l0aXZlIDwtIHJlcCgwLCBucm93KHZpcnVzZXMpKQp2aXJ1c2VzJHRydWVwb3NpdGl2ZVt2aXJ1c2VzJHNlcXR5cGU9PSJ2aXJ1cyJdIDwtIDEKYGBgCgoKYGBge3J9CnJvY29iaiA8LSByb2ModmlydXNlcyR0cnVlcG9zaXRpdmUsIHZpcnVzZXMka2VlcF9zY29yZSkKcm9jb2JqX2FsbCA8LSByb2ModmlydXNlcyR0cnVlcG9zaXRpdmUsIHZpcnVzZXMka2VlcF9zY29yZV9hbGwpCmF1YyA8LSByb3VuZChhdWModmlydXNlcyR0cnVlcG9zaXRpdmUsIHZpcnVzZXMka2VlcF9zY29yZSksNCkKYXVjX2FsbCA8LSByb3VuZChhdWModmlydXNlcyR0cnVlcG9zaXRpdmUsIHZpcnVzZXMka2VlcF9zY29yZV9hbGwpLDQpCiNjcmVhdGUgUk9DIHBsb3QKZ2dyb2Mocm9jb2JqLCBjb2xvdXIgPSAnc3RlZWxibHVlJywgc2l6ZSA9IDIpICsKICBnZ3RpdGxlKHBhc3RlMCgnUk9DIEN1cnZlICcsICcoQVVDID0gJywgYXVjLCAnKScpKSArCiAgY29vcmRfZXF1YWwoKQpnZ3JvYyhyb2NvYmpfYWxsLCBjb2xvdXIgPSAnZ3JlZW4nLCBzaXplID0gMikgKwogIGdndGl0bGUocGFzdGUwKCdST0MgQ3VydmUgJywgJyhBVUMgPSAnLCBhdWNfYWxsLCAnKScpKQpgYGAKU2Vuc2l0aXZpdHk6IFRoZSBwcm9iYWJpbGl0eSB0aGF0IHRoZSBtb2RlbCBwcmVkaWN0cyBhIHBvc2l0aXZlIG91dGNvbWUgZm9yIGFuIG9ic2VydmF0aW9uIHdoZW4gaW5kZWVkIHRoZSBvdXRjb21lIGlzIHBvc2l0aXZlLgpTcGVjaWZpY2l0eTogVGhlIHByb2JhYmlsaXR5IHRoYXQgdGhlIG1vZGVsIHByZWRpY3RzIGEgbmVnYXRpdmUgb3V0Y29tZSBmb3IgYW4gb2JzZXJ2YXRpb24gd2hlbiBpbmRlZWQgdGhlIG91dGNvbWUgaXMgbmVnYXRpdmUuCgoKCgojIENvbXBhcmluZyBiZWhhdmlvciBvZiBhbGwgdGVzdGluZyBzZXRzIGNvbWJpbmVkIChjbHVzdGVyaW5nIGFuYWx5c2VzKQoKYGBge3J9CnZpcmFsX3Njb3JlcyA8LSBtYXRyaXgoZGF0YT0wLCBucm93PW5yb3codmlydXNlcyksIG5jb2w9bnJvdyhjb21ib3NfbGlzdCkpCm51bV92aXJ1c2VzIDwtIGRhdGEuZnJhbWUodG9vbGNvbWJvPXJlcCgwLCBucm93KGNvbWJvc19saXN0KSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgbnVtX3ZpcnVzZXM9cmVwKDAsIG5yb3coY29tYm9zX2xpc3QpKSkKCmZvciAoaSBpbiAxOm5yb3coY29tYm9zX2xpc3QpKSB7CiAgdmlyYWxfc2NvcmVzWyxpXSA8LSBnZXR0aW5nX3ZpcmFsX3NldF8xKHZpcnVzZXMsIGluY2x1ZGVfdmlicmFudCA9IGNvbWJvc19saXN0JFZJQlJBTlRbaV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaW5jbHVkZV92aXJzb3J0ZXIgPSBjb21ib3NfbGlzdCRWU1tpXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpbmNsdWRlX3ZpcnNvcnRlcjIgPSBjb21ib3NfbGlzdCRWUzJbaV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaW5jbHVkZV90dW5pbmcgPSBjb21ib3NfbGlzdCRDaGVja1ZbaV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaW5jbHVkZV9rYWlqdSA9IGNvbWJvc19saXN0JEthaWp1W2ldLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGluY2x1ZGVfZGVlcHZpcmZpbmRlciA9IGNvbWJvc19saXN0JERWRltpXSkKICAKICBudW1fdmlydXNlcyRudW1fdmlydXNlc1tpXSA8LSB0YWJsZSh2aXJhbF9zY29yZXNbLGldPj0xKVtbMl1dCiAgCiAgbnVtX3ZpcnVzZXMkdG9vbGNvbWJvW2ldIDwtIGNvbWJvc19saXN0JHRvb2xjb21ib1tpXQogIAogIG51bV92aXJ1c2VzJHRvb2xjb21ibzJbaV0gPC0gY29tYm9zX2xpc3QkdG9vbGNvbWJvMltpXQp9CgpudW1fdmlydXNlcyRudW10b29scyA8LSBzdHJfY291bnQobnVtX3ZpcnVzZXMkdG9vbGNvbWJvLCAiMSIpCm51bV92aXJ1c2VzIDwtIG51bV92aXJ1c2VzW29yZGVyKG51bV92aXJ1c2VzJG51bV92aXJ1c2VzLCBkZWNyZWFzaW5nPUYpLF0KbnVtX3ZpcnVzZXMkdG9vbGNvbWJvIDwtIGZhY3RvcihudW1fdmlydXNlcyR0b29sY29tYm8sIGxldmVscyA9IHVuaXF1ZShudW1fdmlydXNlcyR0b29sY29tYm8pKQpudW1fdmlydXNlcyR0b29sY29tYm8yIDwtIGZhY3RvcihudW1fdmlydXNlcyR0b29sY29tYm8yLCBsZXZlbHMgPSB1bmlxdWUobnVtX3ZpcnVzZXMkdG9vbGNvbWJvMikpCm51bV92aXJ1c2VzJG51bXRvb2xzIDwtIGFzLmZhY3RvcihudW1fdmlydXNlcyRudW10b29scykKYGBgCgoKYGBge3J9CmdncGxvdChudW1fdmlydXNlcywgYWVzKHg9dG9vbGNvbWJvLCB5PW51bV92aXJ1c2VzLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yPW51bXRvb2xzLCBmaWxsPW51bXRvb2xzKSkgKwogIGdlb21fcG9pbnQoKSArCiAgdGhlbWVfbGlnaHQoKSArCiAgdGhlbWUoCiAgICBwYW5lbC5ncmlkLm1ham9yLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X2JsYW5rKCksCiAgICBheGlzLnRpY2tzLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICBsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwKICAgIGF4aXMudGV4dC55PWVsZW1lbnRfdGV4dChzaXplPTE0KSwKICAgIGF4aXMudGV4dC54PWVsZW1lbnRfdGV4dChzaXplPTE0LCBhbmdsZSA9IDkwKSwKICAgIGxlZ2VuZC50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTEyKSwKICAgIGF4aXMudGl0bGU9ZWxlbWVudF90ZXh0KHNpemU9MTYpLAogICkgKwogIHhsYWIoIlRvb2wgQ29tYmluYXRpb24gKENWLCBEVkYsIEtKLCBWQiwgVlMsIFZTMikiKSArCiAgeWxhYigiTnVtIFZpcnVzZXMgUHJlZGljdGVkIikKCmdncGxvdChudW1fdmlydXNlcywgYWVzKHg9dG9vbGNvbWJvMiwgeT1udW1fdmlydXNlcywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvcj1udW10b29scywgZmlsbD1udW10b29scykpICsKICBnZW9tX3BvaW50KCkgKwogIHRoZW1lX2xpZ2h0KCkgKwogIHRoZW1lKAogICAgcGFuZWwuZ3JpZC5tYWpvci55ID0gZWxlbWVudF9ibGFuaygpLAogICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9ibGFuaygpLAogICAgYXhpcy50aWNrcy55ID0gZWxlbWVudF9ibGFuaygpLAogICAgbGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsCiAgICBheGlzLnRleHQueT1lbGVtZW50X3RleHQoc2l6ZT0xNCksCiAgICBheGlzLnRleHQueD1lbGVtZW50X3RleHQoc2l6ZT0xNCwgYW5nbGUgPSA5MCksCiAgICBsZWdlbmQudGV4dD1lbGVtZW50X3RleHQoc2l6ZT0xMiksCiAgICBheGlzLnRpdGxlPWVsZW1lbnRfdGV4dChzaXplPTE2KSwKICApICsKICB4bGFiKCJUb29sIENvbWJpbmF0aW9uIChDViwgRFZGLCBLSiwgVkIsIFZTLCBWUzIpIikgKwogIHlsYWIoIk51bSBWaXJ1c2VzIFByZWRpY3RlZCIpCmBgYAoKYGBge3J9CmdncGxvdChudW1fdmlydXNlcywgYWVzKHg9bnVtdG9vbHMsIHk9bnVtX3ZpcnVzZXMpKSArCiAgZ2VvbV9ib3hwbG90KGFlcyhjb2xvcj1udW10b29scykpICsKICBnZW9tX3BvaW50KGFlcyhjb2xvcj1udW10b29scywgZmlsbD1udW10b29scykpICsKICB0aGVtZV9saWdodCgpICsKICB0aGVtZSgKICAgIHBhbmVsLmdyaWQubWFqb3IueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIGF4aXMudGlja3MueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iLAogICAgYXhpcy50ZXh0Lnk9ZWxlbWVudF90ZXh0KHNpemU9MTQpLAogICAgYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KHNpemU9MTQsIGFuZ2xlID0gOTApLAogICAgbGVnZW5kLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9MTIpLAogICAgYXhpcy50aXRsZT1lbGVtZW50X3RleHQoc2l6ZT0xNiksCiAgKSArCiAgeGxhYigiTnVtYmVyIG9mIFRvb2xzIikgKwogIHlsYWIoIk51bSBWaXJ1c2VzIFByZWRpY3RlZCIpCmBgYAoKCmBgYHtyfQp2aXJhbF9zY29yZXNfbm96ZXJvcyA8LSB2aXJhbF9zY29yZXNbcm93U3Vtcyh2aXJhbF9zY29yZXMpPjAsXQp2aXJhbF9zY29yZXNfbm96ZXJvcyA8LSB2aXJhbF9zY29yZXNfbm96ZXJvcyArIDEKdmlyYWxfc2NvcmVzX25vemVyb3MgPC0gYXMuZGF0YS5mcmFtZSh2aXJhbF9zY29yZXNfbm96ZXJvcykKCmNvbG5hbWVzKHZpcmFsX3Njb3Jlc19ub3plcm9zKSA8LSBudW1fdmlydXNlcyR0b29sY29tYm8yCmBgYAoKYGBge3J9CmxpYnJhcnkocGh5bG9zZXEpCmBgYAoKCmBgYHtyfQp0b29sZGF0YSA8LSBudW1fdmlydXNlcwoKcm93bmFtZXModG9vbGRhdGEpIDwtIHRvb2xkYXRhJHRvb2xjb21ibzIKYGBgCgpgYGB7cn0KcGh5c2VxX3Bvb2xlZCA8LSBwaHlsb3NlcShvdHVfdGFibGUodmlyYWxfc2NvcmVzX25vemVyb3MsIHRheGFfYXJlX3Jvd3MgPSBUKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNhbXBsZV9kYXRhKHRvb2xkYXRhKSkKYGBgCgpgYGB7cn0Kb3JkaW5hdGlvbiA8LSBwaHlsb3NlcTo6b3JkaW5hdGUocGh5c2VxID1waHlzZXFfcG9vbGVkLCBtZXRob2QgPSAiUENvQSIsIGRpc3RhbmNlID0gImJyYXkiKQpwaHlsb3NlcTo6cGxvdF9vcmRpbmF0aW9uKHBoeXNlcSA9IHBoeXNlcV9wb29sZWQsIG9yZGluYXRpb24gPSBvcmRpbmF0aW9uLAogICAgICAgICAgICAgICAgICAgICAgICAgIHNoYXBlPSJudW10b29scyIsIGNvbG9yPSJudW1fdmlydXNlcyIpICsgCiAgZ2VvbV9wb2ludChzaXplID0gMykgKwogIHRoZW1lX2J3KCkgKwogIGdlb21fbGFiZWwobGFiZWw9dG9vbGRhdGEkdG9vbGNvbWJvKQoKcGh5bG9zZXE6OnBsb3Rfb3JkaW5hdGlvbihwaHlzZXEgPSBwaHlzZXFfcG9vbGVkLCBvcmRpbmF0aW9uID0gb3JkaW5hdGlvbiwKICAgICAgICAgICAgICAgICAgICAgICAgICBzaGFwZT0ibnVtdG9vbHMiLCBjb2xvcj0ibnVtX3ZpcnVzZXMiKSArIAogIGdlb21fcG9pbnQoc2l6ZSA9IDMpICsKICB0aGVtZV9idygpCmBgYAp0byBkbzogdHJ5IGNvbG9yaW5nIGFib3ZlIGJhc2VkIG9uIHRoZSBGMSBzY29yZXMgb2YgdGhlIHRlc3Rpbmcgc2V0IG9uIGVhY2ggY29tYmluYXRpb24KCmBgYHtyfQpicmF5X2Rpc3QgPC0gcGh5bG9zZXE6OmRpc3RhbmNlKHBoeXNlcV9wb29sZWQsIG1ldGhvZD0iYnJheSIpCmNsdXN0ZXJzIDwtIGhjbHVzdChkaXN0KGJyYXlfZGlzdCkpCnBsb3QoY2x1c3RlcnMpCgpteWNsdXN0ZXJzIDwtIGN1dHJlZShjbHVzdGVycywgaD0xLjEpCmBgYAoKCmBgYHtyfQpuYW1lcyhteWNsdXN0ZXJzW215Y2x1c3RlcnM9PTFdKQpuYW1lcyhteWNsdXN0ZXJzW215Y2x1c3RlcnM9PTJdKQpuYW1lcyhteWNsdXN0ZXJzW215Y2x1c3RlcnM9PTNdKQpuYW1lcyhteWNsdXN0ZXJzW215Y2x1c3RlcnM9PTRdKQpuYW1lcyhteWNsdXN0ZXJzW215Y2x1c3RlcnM9PTVdKQoKbXljbHVzdGVyc19kZiA8LSB0aWJibGUoY29tYm89bmFtZXMobXljbHVzdGVycyksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjbHVzdGVyX2luZGV4PW15Y2x1c3RlcnMpCgpteWNsdXN0ZXJzX2RmIDwtIHNlcGFyYXRlKG15Y2x1c3RlcnNfZGYsIGNvbD1jb21ibywgaW50bz1jKCJDaGVja1YiLCAiRFZGIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkthaWp1IiwgIlZJQlJBTlQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiVmlyU29ydGVyIiwgIlZpclNvcnRlcjIiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICBzZXA9IiAiLCByZW1vdmUgPSBGKQoKCnRvb2xfY291bnQgPC0gYXMuZGF0YS5mcmFtZShyYmluZCh0YWJsZShteWNsdXN0ZXJzX2RmJENoZWNrViwgbXljbHVzdGVyc19kZiRjbHVzdGVyX2luZGV4KVsyLF0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgdGFibGUobXljbHVzdGVyc19kZiREVkYsIG15Y2x1c3RlcnNfZGYkY2x1c3Rlcl9pbmRleClbMixdLAogICAgICAgICAgICAgICAgICAgICAgICAgIHRhYmxlKG15Y2x1c3RlcnNfZGYkS2FpanUsIG15Y2x1c3RlcnNfZGYkY2x1c3Rlcl9pbmRleClbMixdLAogICAgICAgICAgICAgICAgICAgICAgICAgIHRhYmxlKG15Y2x1c3RlcnNfZGYkVklCUkFOVCwgbXljbHVzdGVyc19kZiRjbHVzdGVyX2luZGV4KVsyLF0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgdGFibGUobXljbHVzdGVyc19kZiRWaXJTb3J0ZXIsIG15Y2x1c3RlcnNfZGYkY2x1c3Rlcl9pbmRleClbMixdLAogICAgICAgICAgICAgICAgICAgICAgICAgIHRhYmxlKG15Y2x1c3RlcnNfZGYkVmlyU29ydGVyMiwgbXljbHVzdGVyc19kZiRjbHVzdGVyX2luZGV4KVsyLF0pCiAgICAgICAgICAgICAgICAgICAgKQoKdG9vbF9jb3VudCRtZXRob2QgPC0gYygiQ2hlY2tWIiwgIkRWRiIsICJLYWlqdSIsICJWSUJSQU5UIiwgIlZpclNvcnRlciIsICJWaXJTb3J0ZXIyIikKCnRvb2xfY291bnQgPC0gbWVsdCh0b29sX2NvdW50KQoKY29sbmFtZXModG9vbF9jb3VudCkgPC0gYygidG9vbCIsICJjbHVzdGVyX2luZGV4IiwgInRvb2xfY291bnQiKQpgYGAKCmBgYHtyfQpwYWwgPC0gZ2d0aGVtZXM6OnRhYmxlYXVfY29sb3JfcGFsKHBhbGV0dGU9IlRhYmxlYXUgMTAiLCB0eXBlPSJyZWd1bGFyIikKCmdncGxvdCh0b29sX2NvdW50LCBhZXMoeD1jbHVzdGVyX2luZGV4LCB5PXRvb2xfY291bnQsCiAgICAgICAgICAgICAgICAgICBmaWxsPWNsdXN0ZXJfaW5kZXgsCiAgICAgICAgICAgICAgICAgICBjb2xvcj1jbHVzdGVyX2luZGV4KSkgKwogIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5IikgKwogIHRoZW1lX2xpZ2h0KCkgKwogIHRoZW1lKAogICAgcGFuZWwuZ3JpZC5tYWpvci55ID0gZWxlbWVudF9ibGFuaygpLAogICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9ibGFuaygpLAogICAgYXhpcy50aWNrcy55ID0gZWxlbWVudF9ibGFuaygpLAogICAgbGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsCiAgICBheGlzLnRleHQueT1lbGVtZW50X3RleHQoc2l6ZT0xNCksCiAgICBheGlzLnRleHQueD1lbGVtZW50X3RleHQoc2l6ZT0xNCksCiAgICBsZWdlbmQudGV4dD1lbGVtZW50X3RleHQoc2l6ZT0xMiksCiAgICBheGlzLnRpdGxlPWVsZW1lbnRfdGV4dChzaXplPTE2KSwKICApICsKICBzY2FsZV9maWxsX21hbnVhbChuYW1lPSIiLAogICAgICAgICAgICAgICAgICAgICB2YWx1ZXMgPSBhbHBoYShyZXYocGFsKDYpKSwgMC41KSkgKwogIHNjYWxlX2NvbG9yX21hbnVhbChuYW1lPSIiLAogICAgICAgICAgICAgICAgICAgICB2YWx1ZXMgPSBhbHBoYShyZXYocGFsKDYpKSwgMSkpICsKICB4bGFiKCJDbHVzdGVyIikgKwogIHlsYWIoIk51bWJlciBvZiBUaW1lcyBpbiBDbHVzdGVyIikgKyAKICBmYWNldF93cmFwKH50b29sLCBzY2FsZXMgPSAiZnJlZSIpCmBgYAoKCmBgYHtyfQogZ2dwbG90KHZpcnVzZXMsIGFlcyh4PWNoZWNrdl92aXJhbF9nZW5lcywgeT1jb25mdXNpb25fbWF0cml4X2hpZ2hfcHJlY2lzaW9uLAogICAgICAgICAgICAgICAgICAgZmlsbD1jb25mdXNpb25fbWF0cml4X2hpZ2hfcHJlY2lzaW9uLAogICAgICAgICAgICAgICAgICAgY29sb3I9Y29uZnVzaW9uX21hdHJpeF9oaWdoX3ByZWNpc2lvbikpICsKICBnZW9tX2JveHBsb3QoYWxwaGE9MC4zKSArCiAgdGhlbWVfbGlnaHQoKSArCiAgdGhlbWUoCiAgICBwYW5lbC5ncmlkLm1ham9yLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X2JsYW5rKCksCiAgICBheGlzLnRpY2tzLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICBsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwKICAgIGF4aXMudGV4dC55PWVsZW1lbnRfdGV4dChzaXplPTE0KSwKICAgIGF4aXMudGV4dC54PWVsZW1lbnRfdGV4dChzaXplPTE0KSwKICAgIGxlZ2VuZC50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTEyKSwKICAgIGF4aXMudGl0bGU9ZWxlbWVudF90ZXh0KHNpemU9MTYpLAogICkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKG5hbWU9IiIsCiAgICAgICAgICAgICAgICAgICAgIHZhbHVlcyA9IGFscGhhKHJldihwYWwoNCkpLCAwLjUpLAogICAgICAgICAgICAgICAgICAgIGxhYmVscz1jKCJmYWxzZSBuZWdhdGl2ZSIsICJmYWxzZSBwb3NpdGl2ZSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0cnVlIG5lZ2F0aXZlIiwgInRydWUgcG9zaXRpdmUiKSkgKwogIHNjYWxlX2NvbG9yX21hbnVhbChuYW1lPSIiLAogICAgICAgICAgICAgICAgICAgICB2YWx1ZXMgPSBhbHBoYShyZXYocGFsKDQpKSwgMSksCiAgICAgICAgICAgICAgICAgICAgbGFiZWxzPWMoImZhbHNlIG5lZ2F0aXZlIiwgImZhbHNlIHBvc2l0aXZlIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInRydWUgbmVnYXRpdmUiLCAidHJ1ZSBwb3NpdGl2ZSIpKSArCiAgeGxhYigiTnVtYmVyIG9mIFZpcmFsIFNlcXVlbmNlcyIpICsKICB5bGFiKCIiKSArIAogIGZhY2V0X3dyYXAofnNlcXR5cGUsIHNjYWxlcyA9ICJmcmVlIikgKwogIGNvb3JkX2ZsaXAoKQoKIGdncGxvdCh2aXJ1c2VzLCBhZXMoeD1wZXJjZW50X3ZpcmFsLCB5PWNvbmZ1c2lvbl9tYXRyaXhfaGlnaF9wcmVjaXNpb24sCiAgICAgICAgICAgICAgICAgICBmaWxsPWNvbmZ1c2lvbl9tYXRyaXhfaGlnaF9wcmVjaXNpb24sCiAgICAgICAgICAgICAgICAgICBjb2xvcj1jb25mdXNpb25fbWF0cml4X2hpZ2hfcHJlY2lzaW9uKSkgKwogIGdlb21fYm94cGxvdChhbHBoYT0wLjMpICsKICB0aGVtZV9saWdodCgpICsKICB0aGVtZSgKICAgIHBhbmVsLmdyaWQubWFqb3IueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIGF4aXMudGlja3MueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iLAogICAgYXhpcy50ZXh0Lnk9ZWxlbWVudF90ZXh0KHNpemU9MTQpLAogICAgYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KHNpemU9MTQpLAogICAgbGVnZW5kLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9MTIpLAogICAgYXhpcy50aXRsZT1lbGVtZW50X3RleHQoc2l6ZT0xNiksCiAgKSArCiAgc2NhbGVfZmlsbF9tYW51YWwobmFtZT0iIiwKICAgICAgICAgICAgICAgICAgICAgdmFsdWVzID0gYWxwaGEocmV2KHBhbCg0KSksIDAuNSksCiAgICAgICAgICAgICAgICAgICAgbGFiZWxzPWMoImZhbHNlIG5lZ2F0aXZlIiwgImZhbHNlIHBvc2l0aXZlIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInRydWUgbmVnYXRpdmUiLCAidHJ1ZSBwb3NpdGl2ZSIpKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKG5hbWU9IiIsCiAgICAgICAgICAgICAgICAgICAgIHZhbHVlcyA9IGFscGhhKHJldihwYWwoNCkpLCAxKSwKICAgICAgICAgICAgICAgICAgICBsYWJlbHM9YygiZmFsc2UgbmVnYXRpdmUiLCAiZmFsc2UgcG9zaXRpdmUiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHJ1ZSBuZWdhdGl2ZSIsICJ0cnVlIHBvc2l0aXZlIikpICsKICB4bGFiKCJQZXJjZW50IEdlbmVzIFZpcmFsIikgKwogIHlsYWIoIiIpICsgCiAgZmFjZXRfd3JhcCh+c2VxdHlwZSwgc2NhbGVzID0gImZyZWUiKSArCiAgY29vcmRfZmxpcCgpCgogZ2dwbG90KHZpcnVzZXMsIGFlcyh4PWhhbGxtYXJrLCB5PWNvbmZ1c2lvbl9tYXRyaXhfaGlnaF9wcmVjaXNpb24sCiAgICAgICAgICAgICAgICAgICBmaWxsPWNvbmZ1c2lvbl9tYXRyaXhfaGlnaF9wcmVjaXNpb24sCiAgICAgICAgICAgICAgICAgICBjb2xvcj1jb25mdXNpb25fbWF0cml4X2hpZ2hfcHJlY2lzaW9uKSkgKwogIGdlb21fYm94cGxvdChhbHBoYT0wLjMpICsKICB0aGVtZV9saWdodCgpICsKICB0aGVtZSgKICAgIHBhbmVsLmdyaWQubWFqb3IueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIGF4aXMudGlja3MueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iLAogICAgYXhpcy50ZXh0Lnk9ZWxlbWVudF90ZXh0KHNpemU9MTQpLAogICAgYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KHNpemU9MTQpLAogICAgbGVnZW5kLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9MTIpLAogICAgYXhpcy50aXRsZT1lbGVtZW50X3RleHQoc2l6ZT0xNiksCiAgKSArCiAgc2NhbGVfZmlsbF9tYW51YWwobmFtZT0iIiwKICAgICAgICAgICAgICAgICAgICAgdmFsdWVzID0gYWxwaGEocmV2KHBhbCg0KSksIDAuNSksCiAgICAgICAgICAgICAgICAgICAgbGFiZWxzPWMoImZhbHNlIG5lZ2F0aXZlIiwgImZhbHNlIHBvc2l0aXZlIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInRydWUgbmVnYXRpdmUiLCAidHJ1ZSBwb3NpdGl2ZSIpKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKG5hbWU9IiIsCiAgICAgICAgICAgICAgICAgICAgIHZhbHVlcyA9IGFscGhhKHJldihwYWwoNCkpLCAxKSwKICAgICAgICAgICAgICAgICAgICBsYWJlbHM9YygiZmFsc2UgbmVnYXRpdmUiLCAiZmFsc2UgcG9zaXRpdmUiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHJ1ZSBuZWdhdGl2ZSIsICJ0cnVlIHBvc2l0aXZlIikpICsKICB4bGFiKCJOdW1iZXIgb2YgSGFsbG1hcmsgR2VuZXMiKSArCiAgeWxhYigiIikgKyAKICBmYWNldF93cmFwKH5zZXF0eXBlLCBzY2FsZXMgPSAiZnJlZSIpICsKICBjb29yZF9mbGlwKCkKIApnZ3Bsb3QodmlydXNlcywgYWVzKHg9aGFsbG1hcmssIHk9Y2hlY2t2X3ZpcmFsX2dlbmVzLAogICAgICAgICAgICAgICAgICAgZmlsbD1jb25mdXNpb25fbWF0cml4X2hpZ2hfcHJlY2lzaW9uLAogICAgICAgICAgICAgICAgICAgY29sb3I9Y29uZnVzaW9uX21hdHJpeF9oaWdoX3ByZWNpc2lvbikpICsKICBnZW9tX3BvaW50KGFscGhhPTAuMykgKwogIHRoZW1lX2xpZ2h0KCkgKwogIHRoZW1lKAogICAgcGFuZWwuZ3JpZC5tYWpvci55ID0gZWxlbWVudF9ibGFuaygpLAogICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9ibGFuaygpLAogICAgYXhpcy50aWNrcy55ID0gZWxlbWVudF9ibGFuaygpLAogICAgbGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsCiAgICBheGlzLnRleHQueT1lbGVtZW50X3RleHQoc2l6ZT0xNCksCiAgICBheGlzLnRleHQueD1lbGVtZW50X3RleHQoc2l6ZT0xNCksCiAgICBsZWdlbmQudGV4dD1lbGVtZW50X3RleHQoc2l6ZT0xMiksCiAgICBheGlzLnRpdGxlPWVsZW1lbnRfdGV4dChzaXplPTE2KSwKICApICsKICBzY2FsZV9maWxsX21hbnVhbChuYW1lPSIiLAogICAgICAgICAgICAgICAgICAgICB2YWx1ZXMgPSBhbHBoYShyZXYocGFsKDQpKSwgMC41KSwKICAgICAgICAgICAgICAgICAgICBsYWJlbHM9YygiZmFsc2UgbmVnYXRpdmUiLCAiZmFsc2UgcG9zaXRpdmUiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHJ1ZSBuZWdhdGl2ZSIsICJ0cnVlIHBvc2l0aXZlIikpICsKICBzY2FsZV9jb2xvcl9tYW51YWwobmFtZT0iIiwKICAgICAgICAgICAgICAgICAgICAgdmFsdWVzID0gYWxwaGEocmV2KHBhbCg0KSksIDEpLAogICAgICAgICAgICAgICAgICAgIGxhYmVscz1jKCJmYWxzZSBuZWdhdGl2ZSIsICJmYWxzZSBwb3NpdGl2ZSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0cnVlIG5lZ2F0aXZlIiwgInRydWUgcG9zaXRpdmUiKSkgKwogIHhsYWIoIk51bWJlciBvZiBIYWxsbWFyayBHZW5lcyIpICsKICB5bGFiKCJOdW1iZXIgb2YgVmlyYWwgR2VuZXMiKSArIAogIGZhY2V0X3dyYXAofnNlcXR5cGUsIHNjYWxlcyA9ICJmcmVlIikgKwogIGNvb3JkX2ZsaXAoKQpgYGAKCmBgYHtyfQp2aXJ1c2VzX2ZhbHNlX3Bvc2l0aXZlIDwtIHZpcnVzZXNbdmlydXNlcyRjb25mdXNpb25fbWF0cml4X2hpZ2hfcHJlY2lzaW9uPT0iZmFsc2UgcG9zaXRpdmUiLF0KdmlydXNlc19mYWxzZV9uZWdhdGl2ZSA8LSB2aXJ1c2VzW3ZpcnVzZXMkY29uZnVzaW9uX21hdHJpeF9oaWdoX3ByZWNpc2lvbj09ImZhbHNlIG5lZ2F0aXZlIixdCmBgYAoKYGBge3J9CmdncGxvdCh2aXJ1c2VzLCBhZXMoeD1oYWxsbWFyaywgeT1jaGVja3ZfdmlyYWxfZ2VuZXMsCiAgICAgICAgICAgICAgICAgICBmaWxsPWNoZWNrdl9sZW5ndGgsCiAgICAgICAgICAgICAgICAgICBjb2xvcj1jaGVja3ZfbGVuZ3RoLAogICAgICAgICAgICAgICAgICAgc2hhcGU9Y2hlY2t2X3Byb3ZpcnVzKSkgKwogIGdlb21fcG9pbnQoYWxwaGE9MC4zKSArCiAgdGhlbWVfbGlnaHQoKSArCiAgdGhlbWUoCiAgICBwYW5lbC5ncmlkLm1ham9yLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X2JsYW5rKCksCiAgICBheGlzLnRpY2tzLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICBsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwKICAgIGF4aXMudGV4dC55PWVsZW1lbnRfdGV4dChzaXplPTE0KSwKICAgIGF4aXMudGV4dC54PWVsZW1lbnRfdGV4dChzaXplPTE0KSwKICAgIGxlZ2VuZC50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTEyKSwKICAgIGF4aXMudGl0bGU9ZWxlbWVudF90ZXh0KHNpemU9MTYpLAogICkgKwogIHhsYWIoIk51bWJlciBvZiBIYWxsbWFyayBHZW5lcyIpICsKICB5bGFiKCJOdW1iZXIgb2YgVmlyYWwgR2VuZXMiKSArIAogIGZhY2V0X3dyYXAofnNlcXR5cGUsIHNjYWxlcyA9ICJmcmVlIikgKwogIGNvb3JkX2ZsaXAoKQoKZ2dwbG90KHZpcnVzZXNfZmFsc2VfcG9zaXRpdmUsIGFlcyh4PWhhbGxtYXJrLCB5PWNoZWNrdl9sZW5ndGgsCiAgICAgICAgICAgICAgICAgICBmaWxsPWNoZWNrdl92aXJhbF9nZW5lcywKICAgICAgICAgICAgICAgICAgIGNvbG9yPWNoZWNrdl92aXJhbF9nZW5lcywKICAgICAgICAgICAgICAgICAgIHNoYXBlPWNoZWNrdl9wcm92aXJ1cykpICsKICBnZW9tX3BvaW50KGFscGhhPTAuMykgKwogIHRoZW1lX2xpZ2h0KCkgKwogIHRoZW1lKAogICAgcGFuZWwuZ3JpZC5tYWpvci55ID0gZWxlbWVudF9ibGFuaygpLAogICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9ibGFuaygpLAogICAgYXhpcy50aWNrcy55ID0gZWxlbWVudF9ibGFuaygpLAogICAgbGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsCiAgICBheGlzLnRleHQueT1lbGVtZW50X3RleHQoc2l6ZT0xNCksCiAgICBheGlzLnRleHQueD1lbGVtZW50X3RleHQoc2l6ZT0xNCksCiAgICBsZWdlbmQudGV4dD1lbGVtZW50X3RleHQoc2l6ZT0xMiksCiAgICBheGlzLnRpdGxlPWVsZW1lbnRfdGV4dChzaXplPTE2KSwKICApICsKICB4bGFiKCJOdW1iZXIgb2YgSGFsbG1hcmsgR2VuZXMiKSArCiAgeWxhYigiQ29udGlnIExlbmd0aCIpICsgCiAgZmFjZXRfd3JhcCh+c2VxdHlwZSwgc2NhbGVzID0gImZyZWUiKSArCiAgY29vcmRfZmxpcCgpCgpnZ3Bsb3QodmlydXNlc19mYWxzZV9wb3NpdGl2ZVt2aXJ1c2VzX2ZhbHNlX3Bvc2l0aXZlJHNlcXR5cGU9PSJiYWN0ZXJpYSJdLCBhZXMoeD1oYWxsbWFyaywgeT1jaGVja3ZfbGVuZ3RoLAogICAgICAgICAgICAgICAgICAgZmlsbD1jaGVja3ZfdmlyYWxfZ2VuZXMsCiAgICAgICAgICAgICAgICAgICBjb2xvcj1jaGVja3ZfdmlyYWxfZ2VuZXMsCiAgICAgICAgICAgICAgICAgICBzaGFwZT1jaGVja3ZfcHJvdmlydXMpKSArCiAgZ2VvbV9wb2ludChhbHBoYT0wLjMpICsKICB0aGVtZV9saWdodCgpICsKICB0aGVtZSgKICAgIHBhbmVsLmdyaWQubWFqb3IueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIGF4aXMudGlja3MueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iLAogICAgYXhpcy50ZXh0Lnk9ZWxlbWVudF90ZXh0KHNpemU9MTQpLAogICAgYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KHNpemU9MTQpLAogICAgbGVnZW5kLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9MTIpLAogICAgYXhpcy50aXRsZT1lbGVtZW50X3RleHQoc2l6ZT0xNiksCiAgKSArCiAgeGxhYigiTnVtYmVyIG9mIEhhbGxtYXJrIEdlbmVzIikgKwogIHlsYWIoIkNvbnRpZyBMZW5ndGgiKSArIAogIGZhY2V0X3dyYXAofkthaWp1X1ZpcmFsLCBzY2FsZXMgPSAiZnJlZSIpICsKICBjb29yZF9mbGlwKCkKCmdncGxvdCh2aXJ1c2VzX2ZhbHNlX3Bvc2l0aXZlW3ZpcnVzZXNfZmFsc2VfcG9zaXRpdmUkc2VxdHlwZT09ImZ1bmdpIl0sIGFlcyh4PWhhbGxtYXJrLCB5PWNoZWNrdl9sZW5ndGgsCiAgICAgICAgICAgICAgICAgICBmaWxsPWtlZXBfc2NvcmVfaGlnaF9wcmVjaXNpb24sCiAgICAgICAgICAgICAgICAgICBjb2xvcj1rZWVwX3Njb3JlX2hpZ2hfcHJlY2lzaW9uLAogICAgICAgICAgICAgICAgICAgc2hhcGU9Y2hlY2t2X3Byb3ZpcnVzKSkgKwogIGdlb21fcG9pbnQoYWxwaGE9MC4zKSArCiAgdGhlbWVfbGlnaHQoKSArCiAgdGhlbWUoCiAgICBwYW5lbC5ncmlkLm1ham9yLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X2JsYW5rKCksCiAgICBheGlzLnRpY2tzLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICBsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwKICAgIGF4aXMudGV4dC55PWVsZW1lbnRfdGV4dChzaXplPTE0KSwKICAgIGF4aXMudGV4dC54PWVsZW1lbnRfdGV4dChzaXplPTE0KSwKICAgIGxlZ2VuZC50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTEyKSwKICAgIGF4aXMudGl0bGU9ZWxlbWVudF90ZXh0KHNpemU9MTYpLAogICkgKwogIHhsYWIoIk51bWJlciBvZiBIYWxsbWFyayBHZW5lcyIpICsKICB5bGFiKCJDb250aWcgTGVuZ3RoIikgKyAKICBmYWNldF93cmFwKH5LYWlqdV9WaXJhbCwgc2NhbGVzID0gImZyZWUiKSArCiAgY29vcmRfZmxpcCgpCgpnZ3Bsb3QodmlydXNlc19mYWxzZV9wb3NpdGl2ZVt2aXJ1c2VzX2ZhbHNlX3Bvc2l0aXZlJHNlcXR5cGU9PSJwcm90aXN0Il0sIGFlcyh4PWhhbGxtYXJrLCB5PWNoZWNrdl9sZW5ndGgsCiAgICAgICAgICAgICAgICAgICBmaWxsPWNoZWNrdl92aXJhbF9nZW5lcywKICAgICAgICAgICAgICAgICAgIGNvbG9yPWNoZWNrdl92aXJhbF9nZW5lcywKICAgICAgICAgICAgICAgICAgIHNoYXBlPWNoZWNrdl9wcm92aXJ1cykpICsKICBnZW9tX3BvaW50KGFscGhhPTAuMykgKwogIHRoZW1lX2xpZ2h0KCkgKwogIHRoZW1lKAogICAgcGFuZWwuZ3JpZC5tYWpvci55ID0gZWxlbWVudF9ibGFuaygpLAogICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9ibGFuaygpLAogICAgYXhpcy50aWNrcy55ID0gZWxlbWVudF9ibGFuaygpLAogICAgbGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsCiAgICBheGlzLnRleHQueT1lbGVtZW50X3RleHQoc2l6ZT0xNCksCiAgICBheGlzLnRleHQueD1lbGVtZW50X3RleHQoc2l6ZT0xNCksCiAgICBsZWdlbmQudGV4dD1lbGVtZW50X3RleHQoc2l6ZT0xMiksCiAgICBheGlzLnRpdGxlPWVsZW1lbnRfdGV4dChzaXplPTE2KSwKICApICsKICB4bGFiKCJOdW1iZXIgb2YgSGFsbG1hcmsgR2VuZXMiKSArCiAgeWxhYigiQ29udGlnIExlbmd0aCIpICsgCiAgZmFjZXRfd3JhcCh+S2FpanVfVmlyYWwsIHNjYWxlcyA9ICJmcmVlIikgKwogIGNvb3JkX2ZsaXAoKQoKZ2dwbG90KHZpcnVzZXNfZmFsc2VfbmVnYXRpdmUsIGFlcyh4PWhhbGxtYXJrLCB5PWNoZWNrdl9sZW5ndGgsCiAgICAgICAgICAgICAgICAgICBmaWxsPWNoZWNrdl92aXJhbF9nZW5lcywKICAgICAgICAgICAgICAgICAgIGNvbG9yPWNoZWNrdl92aXJhbF9nZW5lcywKICAgICAgICAgICAgICAgICAgIHNoYXBlPWNoZWNrdl9wcm92aXJ1cykpICsKICBnZW9tX3BvaW50KGFscGhhPTAuMykgKwogIHRoZW1lX2xpZ2h0KCkgKwogIHRoZW1lKAogICAgcGFuZWwuZ3JpZC5tYWpvci55ID0gZWxlbWVudF9ibGFuaygpLAogICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9ibGFuaygpLAogICAgYXhpcy50aWNrcy55ID0gZWxlbWVudF9ibGFuaygpLAogICAgbGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsCiAgICBheGlzLnRleHQueT1lbGVtZW50X3RleHQoc2l6ZT0xNCksCiAgICBheGlzLnRleHQueD1lbGVtZW50X3RleHQoc2l6ZT0xNCksCiAgICBsZWdlbmQudGV4dD1lbGVtZW50X3RleHQoc2l6ZT0xMiksCiAgICBheGlzLnRpdGxlPWVsZW1lbnRfdGV4dChzaXplPTE2KSwKICApICsKICB4bGFiKCJOdW1iZXIgb2YgSGFsbG1hcmsgR2VuZXMiKSArCiAgeWxhYigiQ29udGlnIExlbmd0aCIpICsgCiAgZmFjZXRfd3JhcCh+S2FpanVfVmlyYWwsIHNjYWxlcyA9ICJmcmVlIikgKwogIGNvb3JkX2ZsaXAoKQoKZ2dwbG90KHZpcnVzZXNfZmFsc2VfbmVnYXRpdmUsIGFlcyh4PWhhbGxtYXJrLCB5PWNoZWNrdl9sZW5ndGgsCiAgICAgICAgICAgICAgICAgICBmaWxsPWtlZXBfc2NvcmVfaGlnaF9wcmVjaXNpb24sCiAgICAgICAgICAgICAgICAgICBjb2xvcj1rZWVwX3Njb3JlX2hpZ2hfcHJlY2lzaW9uLAogICAgICAgICAgICAgICAgICAgc2hhcGU9Y2hlY2t2X3Byb3ZpcnVzKSkgKwogIGdlb21fcG9pbnQoYWxwaGE9MC4zKSArCiAgdGhlbWVfbGlnaHQoKSArCiAgdGhlbWUoCiAgICBwYW5lbC5ncmlkLm1ham9yLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X2JsYW5rKCksCiAgICBheGlzLnRpY2tzLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICBsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwKICAgIGF4aXMudGV4dC55PWVsZW1lbnRfdGV4dChzaXplPTE0KSwKICAgIGF4aXMudGV4dC54PWVsZW1lbnRfdGV4dChzaXplPTE0KSwKICAgIGxlZ2VuZC50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTEyKSwKICAgIGF4aXMudGl0bGU9ZWxlbWVudF90ZXh0KHNpemU9MTYpLAogICkgKwogIHhsYWIoIk51bWJlciBvZiBIYWxsbWFyayBHZW5lcyIpICsKICB5bGFiKCJDb250aWcgTGVuZ3RoIikgKyAKICBmYWNldF93cmFwKH5LYWlqdV9WaXJhbCwgc2NhbGVzID0gImZyZWUiKSArCiAgY29vcmRfZmxpcCgpCmBgYAoKCgoKYGBge3J9CnRhYmxlKHZpcnVzZXMkaGFsbG1hcmtbdmlydXNlcyRjb25mdXNpb25fbWF0cml4X2hpZ2hfcHJlY2lzaW9uPT0iZmFsc2UgcG9zaXRpdmUiXT4wKQoKdGFibGUodmlydXNlcyRwZXJjZW50X2hvc3RbdmlydXNlcyRjb25mdXNpb25fbWF0cml4X2hpZ2hfcHJlY2lzaW9uPT0iZmFsc2UgcG9zaXRpdmUiXTw1MCkKYGBgCg==